<?php

namespace common\service;

use common\exception\LogicException;
use common\model\DelayOrderModel;
use common\model\DelayOrderTaskModel;
use common\model\DelayOrderTaskStepCategoryConfirmModel;
use common\model\DemandPoolReportModel;
use common\model\DocumentRelationModel;
use common\model\EntityEpisodeRelationModel;
use common\model\EntityModel;
use common\model\MediaModel;
use common\model\ModuleModel;
use common\model\NoteModel;
use common\model\OrderModel;
use common\model\OrderTaskListModel;
use common\model\OriginalPlanNodeDailyReportModel;
use common\model\OriginalPlanNodeDetailModel;
use common\model\OriginalPlanNodeModel;
use common\model\PlanModel;
use common\model\PlanTaskRelationModel;
use common\model\PlaylistVersionModel;
use common\model\ProjectMemberModel;
use common\model\ProjectModel;
use common\model\ProjectTemplateModel;
use common\model\ReviewFeedbackEntityModel;
use common\model\ReviewFeedbackModel;
use common\model\ReviewRequestModel;
use common\model\ReviewRequestNodeModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use common\model\StepWorkflowModel;
use common\model\TaskMemberModel;
use common\model\TaskModel;
use common\model\TaskRelationModel;
use common\model\TaskRelationTagModel;
use common\model\TaskWorkOrderModel;
use common\model\TaskWorkOrderTaskListModel;
use common\model\TaskWorkOrderUserModel;
use common\model\TimelogModel;
use common\model\UserModel;
use common\model\WorkTimeRecordModel;
use common\observer\TaskExecutorTypeSyncToEntity;
use support\bootstrap\Redis;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use Webman\Stomp\Client;

class FixDataService
{
    use SingletonTrait;

    public function __construct()
    {
    }

    /**
     * 重写工序标准目录和文档 (不会删除之前的post  如果需要删除 请在mysql中删除book_type_id的所有内容)
     * @return array
     * @throws \Exception
     */
    public function stepStandardsData()
    {
        $allDocumentRelationData = model(DocumentRelationModel::class)->where(['relation_type' => 'task_standard'])->select();
        $projectTemplateIdList = array_column($allDocumentRelationData, 'project_template_id', 'project_template_id');
        $stepWorkflowIdList = array_column($allDocumentRelationData, 'step_workflow_id', 'step_workflow_id');
        $stepIdList = array_column($allDocumentRelationData, 'step_id', 'step_id');
        $postIdList = array_column($allDocumentRelationData, 'post_id', 'post_id');
        $postIdList = array_values($postIdList);

        $allDocumentRelationData = array_group_by($allDocumentRelationData, 'project_template_id', 'step_workflow_id', 'step_id');
        $optionService = OptionsService::getInstance();
        $bookTypeId = $optionService->getStepStandardBookTypeId();
        $projectTemplateList = model(ProjectTemplateModel::class)->where(['id' => ['IN', $projectTemplateIdList]])->field('id,name')->select();
        $projectTemplateList = array_column($projectTemplateList, 'name', 'id');
        $stepWorkflowList = model(StepWorkflowModel::class)->where(['id' => ['IN', $stepWorkflowIdList]])->field('id,name')->select();
        $stepWorkflowList = array_column($stepWorkflowList, 'name', 'id');
        $stepList = model(StepModel::class)->where(['id' => ['IN', $stepIdList]])->field('id,name')->select();
        $stepList = array_column($stepList, 'name', 'id');
        $postList = teamones_request('teamones-doc', 'post/select', ['param' => ['filter' => ['id' => ['-in', implode(',', $postIdList)]]]]);
        $postList = array_column($postList['data'], null, 'id');
        $res = [];
        foreach ($allDocumentRelationData as $projectTemplateId => $relationGroupByStepWorkflow) {
            if (empty($projectTemplateList[$projectTemplateId])) {
                $res[] = "project Template not found " . $projectTemplateId;
                continue;
            }
            foreach ($relationGroupByStepWorkflow as $stepWorkflowId => $relationGroupByStep) {
                if (empty($stepWorkflowList[$stepWorkflowId])) {
                    $res[] = "step workflow not found " . $stepWorkflowId;
                    continue;
                }
                $posts = [
                    [
                        'name' => $projectTemplateList[$projectTemplateId],
                        'content' => '',
                        'file_type' => 3,
                    ],
                    [
                        'name' => $stepWorkflowList[$stepWorkflowId],
                        'content' => '',
                        'file_type' => 3,
                    ],
                ];
                foreach ($relationGroupByStep as $stepId => $relationData) {
                    if (empty($stepList[$stepId])) {
                        $res[] = "step not found " . $stepId;
                        continue;
                    }
                    $relationData = array_column($relationData, null, 'attr_str');
                    foreach ($relationData as $attr => $relationDataItem) {
                        if (empty($postList[$relationDataItem['post_id']])) {
                            $res[] = "post not found " . $relationDataItem['post_id'];
                            continue;
                        }
                        $requestPosts = $posts;
                        if (empty($attr)) {
                            //  说明没有属性
                            $requestPosts[] = [
                                'name' => $stepList[$stepId],
                                'content' => $postList[$relationDataItem['post_id']]['content'],
                                'file_type' => 1,
                            ];
                        } else {
                            $requestPosts[] = [
                                'name' => $stepList[$stepId],
                                'content' => $stepList[$stepId],
                                'file_type' => 3,
                            ];
                            $requestPosts[] = [
                                'name' => $relationDataItem['attr_str'],
                                'content' => $postList[$relationDataItem['post_id']]['content'],
                                'file_type' => 1,
                            ];
                        }
                        $requestData = [
                            'data' => [
                                'book_type_id' => $bookTypeId,
                                'type' => 'catalog_post',
                                'posts' => $requestPosts,
                            ],
                        ];

                        $res[] = $requestData;
                        $post = teamones_request('teamones-doc', 'post/post_bath_create', $requestData);

                        model(DocumentRelationModel::class)->save(['id' => $relationDataItem['id'], 'post_id' => $post['data']]);
                    }
                }
            }
        }
        return $res;
    }

    /**
     * 修复实际审核截至时间
     * @return array
     */
    public function fixPlaylistVersionEndTime()
    {
        //获取审核列表中 审核完了 review_request 最大的时间
        $playlistVersionList = model(PlaylistVersionModel::class)
            ->join('playlist_item_version on playlist_item_version.playlist_version_id = playlist_version.id')
            ->where(['playlist_version.current_review_code' => 'done', 'playlist_version.end_time' => 0])
            ->field('playlist_item_version.review_request_id,playlist_item_version.playlist_version_id')->select();

        //获取审核不为0的review_request
        $reviewRequestIds = [];
        foreach ($playlistVersionList as $playlistVersionListItem) {
            if ($playlistVersionListItem['review_request_id'] > 0 && !in_array($playlistVersionListItem['review_request_id'], $reviewRequestIds)) {
                $reviewRequestIds[] = $playlistVersionListItem['review_request_id'];
            }
        }

        //建立审核映射 id => reviewed
        $reviewRequestMap = [];
        if (!empty($reviewRequestIds)) {
            $reviewRequestList = model(ReviewRequestModel::class)->where(['id' => ['IN', $reviewRequestIds]])->field('id,reviewed')->select();
            $reviewRequestMap = array_column($reviewRequestList, 'reviewed', 'id');
        }

        // 组装审核列表更新时间数据
        $updatePlaylistVersionTime = [];
        foreach ($playlistVersionList as $playlistVersionListItem) {
            if ($playlistVersionListItem['review_request_id'] > 0 && isset($reviewRequestMap[$playlistVersionListItem['review_request_id']])) {
                if (!isset($updatePlaylistVersionTime[$playlistVersionListItem['playlist_version_id']])) {
                    $updatePlaylistVersionTime[$playlistVersionListItem['playlist_version_id']] = [
                        'id' => $playlistVersionListItem['playlist_version_id'],
                        'end_time' => $reviewRequestMap[$playlistVersionListItem['review_request_id']],
                    ];
                } elseif ($updatePlaylistVersionTime[$playlistVersionListItem['playlist_version_id']]['end_time'] < $reviewRequestMap[$playlistVersionListItem['review_request_id']]) {
                    $updatePlaylistVersionTime[$playlistVersionListItem['playlist_version_id']]['end_time'] = $reviewRequestMap[$playlistVersionListItem['review_request_id']];
                }
            }
        }

        if (!empty($updatePlaylistVersionTime)) {
            $updatePlaylistVersionTime = array_values($updatePlaylistVersionTime);
            model(PlaylistVersionModel::class)->saveAll($updatePlaylistVersionTime);
        }

        return $updatePlaylistVersionTime;
    }

    /**
     * 填充项目对应的角色
     * @param $role
     * @return array|string
     * @throws \Exception
     */
    public function fixProjectMemberRole($role)
    {
        //获取项目目前存在的制片用户
        $ProjectMemberList = model(ProjectMemberModel::class)->where(['role_code' => ['IN', $role]])->field('project_id,role_code,user_id')->select();

        // 项目 => 租户 ,  租户=>用户=>租户用户角色

        $projectIds = [];
        $userIds = [];
        foreach ($ProjectMemberList as $ProjectMemberItem) {
            $projectIds[$ProjectMemberItem['project_id']] = $ProjectMemberItem['project_id'];
            $userIds[$ProjectMemberItem['user_id']] = $ProjectMemberItem['user_id'];
        }

        $projectList = model(ProjectModel::class)->where(['id' => ['IN', $projectIds]])->field('id,tenant_id')->select();
        $tenantId = [];
        $projectTenantMap = [];
        foreach ($projectList as $projectItem) {
            $tenantId[$projectItem['tenant_id']] = $projectItem['tenant_id'];
            $projectTenantMap[$projectItem['id']] = $projectItem['tenant_id'];
        }
        $tenantId = array_values($tenantId);
        $userList = model(UserModel::class)->where(['id' => ['IN', $userIds]])->field('id,union_id')->select();
        $unionIds = array_column($userList, 'union_id');

        // 通过 租户用户角色 回填 => 项目用户
        $param = [
            "param" => [
                "filter" => [
                    'tenant_id' => ['-in', join(',', $tenantId)],
                    "user_id" => ['-in', join(',', $unionIds)],
                    "role_code" => $role,
                ],
            ],
        ];
        $selectDepartmentUserList = TenantService::getInstance()->selectDepartmentUserList($param);

        if (empty($selectDepartmentUserList['rows'])) {
            return [];
        }

        $selectDepartmentUserList = $selectDepartmentUserList['rows'];
        // 映射建立 tenant => user => [role]
        $departmentUserRoleMap = [];
        foreach ($selectDepartmentUserList as $selectDepartmentUserItem) {
            if (!empty($selectDepartmentUserItem["saas_user"])) {
                if (!isset($departmentUserRoleMap[$selectDepartmentUserItem['tenant_id']][$selectDepartmentUserItem["saas_user"]['id']])) {
                    $departmentUserRoleMap[$selectDepartmentUserItem['tenant_id']][$selectDepartmentUserItem["saas_user"]['id']] =
                        [$selectDepartmentUserItem['role_code'] => $selectDepartmentUserItem['role_code']];
                } else {
                    $departmentUserRoleMap[$selectDepartmentUserItem['tenant_id']][$selectDepartmentUserItem["saas_user"]['id']][$selectDepartmentUserItem['role_code']]
                        = $selectDepartmentUserItem['role_code'];
                }
            }
        }

        $addProjectMemberData = [];
        foreach ($ProjectMemberList as $ProjectMemberItem) {
            if (isset($departmentUserRoleMap[$projectTenantMap[$ProjectMemberItem['project_id']]][$ProjectMemberItem['user_id']]) &&
                !isset($addProjectMemberData[$ProjectMemberItem['project_id']][$ProjectMemberItem['user_id']])) {
                $addProjectMemberData[$ProjectMemberItem['project_id']][$ProjectMemberItem['user_id']] =
                    $departmentUserRoleMap[$projectTenantMap[$ProjectMemberItem['project_id']]][$ProjectMemberItem['user_id']];
            }

            //去重
            if (isset($addProjectMemberData[$ProjectMemberItem['project_id']][$ProjectMemberItem['user_id']][$ProjectMemberItem['role_code']])) {
                unset($addProjectMemberData[$ProjectMemberItem['project_id']][$ProjectMemberItem['user_id']][$ProjectMemberItem['role_code']]);
            }
        }

        $addData = [];
        foreach ($addProjectMemberData as $projectId => $projectMemberItem) {
            foreach ($projectMemberItem as $userId => $roleCodes) {
                foreach ($roleCodes as $roleCode) {
                    $addData[] = [
                        'project_id' => $projectId,
                        'user_id' => $userId,
                        'role_code' => $roleCode,
                        'uuid' => create_uuid(),
                    ];
                }
            }
        }

        if (!empty($addData)) {
            model(ProjectMemberModel::class)->addAll($addData);
        }

        return $addData;
    }

    /**
     * 填充工单用户数据
     * @return array
     * @throws \Exception
     */
    public function fixTaskWorkOrderUser()
    {
        $model = model(TaskWorkOrderModel::class);
        $taskWorkOrderList = $model->select();
        $taskWorkUserList = model(TaskWorkOrderUserModel::class)->select();
        $taskWorkUserData = [];
        $data = [];
        $addData = [];
        foreach ($taskWorkOrderList as $taskWorkOrder) {
            $data[$taskWorkOrder['id']][] = $taskWorkOrder['created_by'];
            if ($taskWorkOrder['created_by'] != $taskWorkOrder['receiver']) {
                $data[$taskWorkOrder['id']][] = $taskWorkOrder['receiver'];
            }
        }
        foreach ($taskWorkUserList as $user) {
            $taskWorkUserData[$user['work_order_id']][] = $user['user_id'];
        }
        $count = 0;
        foreach ($data as $id => $list) {
            if (!array_key_exists($id, $taskWorkUserData)) {
                foreach ($list as $value) {
                    $addData[$count]['work_order_id'] = $id;
                    $addData[$count]['user_id'] = $value;
                    $addData[$count]['created'] = time();
                    $count++;
                }
            }
        }
        $addData = array_values($addData);
        if (!empty($addData)) {
            model(TaskWorkOrderUserModel::class)->addAll($addData);
        }
        return $addData;
    }

    /**
     * 订单实体信息修复
     * @param $orderId
     * @return array
     * @throws \Exception
     */
    public function orderTaskEntityCheck($orderId)
    {
        $order = model(OrderModel::class)->find($orderId);
        $orderTaskList = model(OrderTaskListModel::class)->where(['order_id' => $orderId])->select();

        $entityList = model(EntityModel::class)->where(['id' => ['IN', array_column($orderTaskList, 'entity_id')]])->select();
        $shadowProjectId = model(ProjectModel::class)->where(['belong_project_id' => $order['project_id'], 'tenant_id' => $order['to_tenant_id']])->value("id");
        if (empty($shadowProjectId)) {
            throw new LogicException('shadow project not found', ErrorCode::PROJECT_NOT_FOUND);
        }
        $entityList = array_column($entityList, null, 'id');
        foreach ($entityList as &$entity) {
            $entity['real_entity_id'] = TaskGenerateService::getInstance()->cloneRelatedEntity($entity['id'], $shadowProjectId, $order['to_tenant_id'], $order['project_id']);
        }
        $realTaskList = model(TaskModel::class)->where(['id' => ['IN', array_column($orderTaskList, 'real_task_id')]])->select();
        if (empty($realTaskList)) {
            throw new LogicException('real task list not found', ErrorCode::TASK_NOT_FOUND);
        }
        $realTaskList = array_column($realTaskList, null, 'id');
        $res = [];
        foreach ($orderTaskList as $orderTaskListItem) {
            $currentEntity = $entityList[$orderTaskListItem['entity_id']] ?? null;
            if (empty($currentEntity)) {
                continue;
            }
            if (empty($currentEntity['real_entity_id'])) {
                continue;
            }
            $realTask = $realTaskList[$orderTaskListItem['real_task_id']] ?? null;
            if (empty($realTask)) {
                continue;
            }

            if ($realTask['entity_id'] != $currentEntity['real_entity_id']) {
                $realEntity = model(EntityModel::class)->find($currentEntity['real_entity_id']);
                $res[] = model(TaskModel::class)->modifyItem(['id' => $realTask['id'], 'entity_id' => $realEntity['id'], 'entity_module_id' => $realEntity['module_id']]);
            }
        }
        return $res;
    }

    /**
     * 将工单的帖子转入到消息中,转换为会话逻辑
     * @return array
     * @throws \think\Exception
     * @throws \Exception
     */
    public function fixWorkNoteToMessage()
    {
        $taskWorkOrderList = model(TaskWorkOrderModel::class)->select();
        $taskWorkOrderIds = array_column($taskWorkOrderList, "id");
        $taskWorkOrderIdMap = array_column($taskWorkOrderList, null, "id");

        //查询未被创建的群聊
        $requestData = [
            "param" => [
                "filter" => [
                    "link_id" => ["-in", join(",", $taskWorkOrderIds)],
                    "link_type" => "task_work_order",
                ],
                "fields" => "link_id",
            ],
        ];
        $workOrderChatList = teamones_request('teamones-im', 'chat/select', $requestData);

        $workOrderChatList = $workOrderChatList['data']['rows'] ?? [];
        $chatLinkId = array_column($workOrderChatList, 'link_id', 'link_id');
        $taskWorkOrderList = array_values(array_diff_key($taskWorkOrderIdMap, $chatLinkId));

        if (empty($taskWorkOrderList)) {
            return [];
        }

        //1.创建群聊
        $userIds = [];
        $taskWorkOrderInitUserMap = [];
        foreach ($taskWorkOrderList as $taskWorkOrderItem) {
            $taskWorkOrderInitUserMap[$taskWorkOrderItem['id']] =
                [$taskWorkOrderItem['created_by'], $taskWorkOrderItem['receiver']];
            $userIds[] = $taskWorkOrderItem['created_by'];
            $userIds[] = $taskWorkOrderItem['receiver'];
        }
        $userIds = array_unique($userIds);
        $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->field("id,union_id")->select();
        $userList = array_column($userList, 'union_id', 'id');

        foreach ($taskWorkOrderInitUserMap as $taskWorkOrderItemId => $taskWorkOrderInitUserMapItem) {
            $currentUnionId = [];
            foreach ($taskWorkOrderInitUserMapItem as $taskWorkOrderInitUserMapItemUserId) {
                $currentUnionId[$userList[$taskWorkOrderInitUserMapItemUserId]] = $userList[$taskWorkOrderInitUserMapItemUserId];
            }
            $taskWorkOrderInitUserMap[$taskWorkOrderItemId] = $currentUnionId;
        }

        $createWorkOrderChatData = [];
        foreach ($taskWorkOrderList as $taskWorkOrderItem) {
            $createWorkOrderChatData[] = [
                'created_by' => $userList[$taskWorkOrderItem['created_by']],
                'receiver' => [$userList[$taskWorkOrderItem['created_by']], $userList[$taskWorkOrderItem['receiver']]],
                'name' => $taskWorkOrderItem["number"] . "({$taskWorkOrderItem['name']})",
                "link_id" => $taskWorkOrderItem["id"],
                "link_type" => "task_work_order",
                "created" => $taskWorkOrderItem["created"],
                "from_tenant_id" => $taskWorkOrderItem["tenant_id"],
            ];
        }

        //首先查询工单是否存在,如果存在相应的群聊,则不在填充

        //利用工单创建群聊
        $chat = teamones_request('teamones-im', 'chat/batch_create_chat', ['data' => $createWorkOrderChatData]);

        if (empty($chat) || empty($chat['data'])) {
            return [];
        }

        $chat = $chat['data'];
        $newChatLinkId = array_unique(array_column($chat, 'link_id'));
        $chatLinkMap = array_column($chat, null, 'link_id');

        //2.添加群聊人员
        $taskWorkOrderUserList = model(TaskWorkOrderUserModel::class)->where(['work_order_id' => ["IN", $newChatLinkId]])->select();
        $taskWorkOrderUserIds = array_unique(array_column($taskWorkOrderUserList, 'user_id'));

        // 查找union_id
        $taskWorkOrderUserMap = model(UserModel::class)->where(['id' => ["IN", $taskWorkOrderUserIds]])->field("id,name,union_id")->select();
        $taskWorkOrderUserMap = array_column($taskWorkOrderUserMap, null, 'id');
        $taskWorkOrderUserList = array_map(function ($taskWorkOrderUserItem) use ($taskWorkOrderUserMap) {
            $taskWorkOrderUserItem['user_name'] = $taskWorkOrderUserMap[$taskWorkOrderUserItem['user_id']]['name'];
            $taskWorkOrderUserItem['user_id'] = $taskWorkOrderUserMap[$taskWorkOrderUserItem['user_id']]['union_id'];
            return $taskWorkOrderUserItem;
        }, $taskWorkOrderUserList);

        //接收人和创建人在创建群聊已经加入,应当去掉
        $taskWorkOrderUserList = array_group_by($taskWorkOrderUserList, 'work_order_id');
        $chatUserData = [];
        foreach ($taskWorkOrderUserList as $linkId => $taskWorkOrderUser) {
            $currentTaskWorkOrderUser = array_column($taskWorkOrderUser, null, 'user_id');
            if (isset($taskWorkOrderInitUserMap[$linkId])) {
                $currentTaskWorkOrderUser = array_diff_key($currentTaskWorkOrderUser, $taskWorkOrderInitUserMap[$linkId]);
            }
            if (empty($currentTaskWorkOrderUser)) {
                $taskWorkOrderUserList[$linkId] = [];
                continue;
            }

            $taskWorkOrderChatId = $chatLinkMap[$linkId]['global_id'];
            foreach ($currentTaskWorkOrderUser as $currentTaskWorkOrderUserItem) {
                $chatUserData[] = [
                    'chat_global_id' => $taskWorkOrderChatId,
                    'user_id' => $currentTaskWorkOrderUserItem['user_id'],
                    'join_at' => $currentTaskWorkOrderUserItem['created'],
                ];
            }

            $taskWorkOrderUserList[$linkId] = $currentTaskWorkOrderUser;
        }

        if (!empty($chatUserData)) {
            $lastChatUser = teamones_request('teamones-im', 'chat_user/add_multiple_user', [
                "data" => $chatUserData,
            ]);
        }

        //3.添加群消息
        //Note存在图片和文字 图片需要转移到im media,message 文字仅需转到message
        $noteList = model(NoteModel::class)
            ->where(['link_id' => ['IN', array_column($taskWorkOrderList, 'id')],
                'module_id' => module_by_code("task_work_order")['id'],
            ])
            ->field("id,json,text,link_id,created_by,created")
            ->order("id asc")
            ->select();

        //找出最小的去掉
        $noteMinList = [];
        $noteList = array_filter($noteList, function ($noteItem) use (&$noteMinList) {
            if (!isset($noteMinList[$noteItem['link_id']])) {
                $noteMinList[$noteItem['link_id']] = $noteItem;
                return false;
            }
            return true;
        });

        $createdBys = [];
        $noteMediaMap = [];
        $medias = [];
        foreach ($noteList as $noteItem) {
            $createdBys[] = $noteItem['created_by'];
            $noteJson = json_decode($noteItem['json'], true);
            if (!empty($noteJson['note_media'])) {
                $noteMedias = explode(",", $noteJson['note_media']);
                $noteMediaMap[$noteItem['id']] = $noteMedias;
                foreach ($noteMedias as $noteMedia) {
                    $medias[] = $noteMedia;
                }
            }
        }

        $createdBys = array_unique($createdBys);
        $medias = array_unique($medias);

        $mediaMap = [];
        if (!empty($medias)) {
            $mediaList = model(MediaModel::class)->where(["id" => ["IN", $medias]])->field("id,thumb,param")->select();
            $mediaMap = array_column($mediaList, null, 'id');
        }

        $createdByMap = [];
        if (!empty($createdBys)) {
            $createdList = model(UserModel::class)->where(["id" => ["IN", $createdBys]])->field("id,union_id")->select();
            $createdByMap = array_column($createdList, 'union_id', 'id');
        }

        $noteList = array_map(function ($noteItem) use ($noteMediaMap, $createdByMap, $mediaMap) {
            //填充图片
            if (!empty($noteMediaMap[$noteItem['id']])) {
                foreach ($noteMediaMap[$noteItem['id']] as $noteMediaMapItem) {
                    if (!empty($mediaMap[$noteMediaMapItem])) {
                        $currentMedia = $mediaMap[$noteMediaMapItem];
                        $currentMediaParam = json_decode($currentMedia['param'], true);
                        $noteItem['note_media'][] = ['url' => $currentMedia['thumb'], 'width' => $currentMediaParam['width'], 'height' => $currentMediaParam['height']];
                    }
                }
            }
            //填充Union_id
            $noteItem['union_id'] = $createdByMap[$noteItem['created_by']];
            return $noteItem;
        }, $noteList);

        $noteList = array_group_by($noteList, 'link_id');
        $messageData = [];
        $newChatLinkData = [];
        //组装Message消息
        foreach ($chat as $chatItem) {
            $currentMessage = $noteList[$chatItem['link_id']] ?? [];
            $newChatLinkData[] = ['id' => $chatItem['link_id'], 'chat_global_id' => $chatItem['global_id']];

            if (!empty($currentMessage)) {
                foreach ($currentMessage as $currentMessageItem) {
                    if (!empty($currentMessageItem['text'])) {
                        $messageData[] = [
                            'global_id' => create_uuid(),
                            'chat_global_id' => $chatItem['global_id'],
                            'content_type' => "text",
                            "sender" => $currentMessageItem['union_id'],
                            'content' => [
                                'content' => $currentMessageItem['text'],
                            ],
                            "sent_at" => $currentMessageItem['created'],
                            "type" => "normal",
                            "class" => 1,
                        ];
                    }

                    if (!empty($currentMessageItem['note_media'])) {
                        foreach ($currentMessageItem['note_media'] as $currentMessageMedia) {
                            $messageData[] = [
                                'global_id' => create_uuid(),
                                'chat_global_id' => $chatItem['global_id'],
                                'content_type' => "image",
                                "sender" => $currentMessageItem['union_id'],
                                'content' => [
                                    'height' => $currentMessageMedia['height'],
                                    'url' => $currentMessageMedia['url'],
                                    'width' => $currentMessageMedia['width'],
                                ],
                                "sent_at" => $currentMessageItem['created'],
                                "type" => "normal",
                                "class" => 2,
                            ];
                        }
                    }
                }
            }

            //填充拉人系统提示消息
            if (!empty($taskWorkOrderUserList[$chatItem['link_id']])) {
                foreach ($taskWorkOrderUserList[$chatItem['link_id']] as $taskWorkOrderUserItem) {
                    $messageData[] = [
                        'global_id' => create_uuid(),
                        "chat_global_id" => $chatItem['global_id'],
                        "content_type" => 'system_hint',
                        'sender' => 0,
                        "content" => [
                            "type" => "invite_chat_user",
                            "replace" => [
                                "leave_user" => [["id" => $taskWorkOrderUserItem['user_id'], "name" => $taskWorkOrderUserItem['user_name']]],
                            ],
                        ],
                        "sent_at" => $taskWorkOrderUserItem["created"],
                        "type" => 'normal',
                        "class" => 3,
                    ];
                }
            }

        }
        //按照类型,时间排序
        multiple_sort_by($messageData, ['sent_at' => 'asc', 'class' => 'asc']);
        $messageData = array_map(function ($messageDataItem) {
            unset($messageDataItem['class']);
            $messageDataItem['content'] = json_encode($messageDataItem['content']);
            return $messageDataItem;
        }, $messageData);

        if (!empty($messageData)) {
            $lastMessage = teamones_request('teamones-im', 'message/add_multiple_message', [
                "data" => $messageData,
            ]);
        }

        // 回填task_work_order chat_global_id
        if (!empty($newChatLinkData)) {
            model(TaskWorkOrderModel::class)->saveAll($newChatLinkData);
        }

        return $messageData;

    }

    /**
     * 填充工单关联任务信息
     * @return bool|int|string
     * @throws \Exception
     */
    public function fixWorkOrderTask()
    {
        $taskData = model(TaskModel::class)->where(['step_type' => 'work_order_to_do'])->field('id,json')->select();
        $workOrder = [];
        // 获取所有工单待办
        foreach ($taskData as &$value) {
            $temp = [];
            $value['json'] = json_decode($value['json'], true);
            if (isset($value['json']['task_work_order_id'])) {
                $temp['task_id'] = $value['id'];
                $temp['work_order_id'] = $value['json']['task_work_order_id'];
                $workOrder[] = $temp;
            }
        }
        $workOrderTaskModel = model(TaskWorkOrderTaskListModel::class);
        $workOrderTaskList = $workOrderTaskModel->select();
        $comparedTaskIdList = array_column($workOrderTaskList, 'task_id');
        // 如果工单任务关联表有数据就对比，没有就直接跳过对比
        if ($comparedTaskIdList) {
            foreach ($workOrder as $key => $row) {
                if (in_array($row['task_id'], $comparedTaskIdList)) {
                    unset($workOrder[$key]);
                }
            }
        }
        return $workOrderTaskModel->addAll($workOrder);
    }

    /**
     * 修复项目部分成员错误的数据
     * @param $projectId
     * @return void
     * @throws \Exception
     */
    public function fixProjectMemberTenant($projectId)
    {
        $res = 0;
        $project = model(ProjectModel::class)->find($projectId);
        $projectMemberModel = model(ProjectMemberModel::class);
        $projectMemberIds = $projectMemberModel->where(['project_id' => $project['id']])->column('user_id');
        if ($projectMemberIds) {
            $model = model(UserModel::class);
            $imUserIds = $model->where(['id' => ['IN', implode(',', $projectMemberIds)]])->column('union_id');
            $requestData = [
                "param" => [
                    "filter" => [
                        "id" => ['-in', implode(',', $imUserIds)],
                    ],
                    'tenant_id' => $project['tenant_id'],
                ],
            ];
            $data = teamones_request('teamones-im', 'user/get_other_users_info', $requestData)['data'];
            $imUserList = array_column(array_column(array_filter($data, function ($item) {
                if ($item['tenant']) {
                    return $item;
                }
            }), 'user'), 'id');
            $diffUsersIds = array_diff($imUserIds, $imUserList);
            if ($diffUsersIds) {
                $saasUserIds = $model->where(['union_id' => ['IN', implode(',', $diffUsersIds)]])->column('id');
                $resIds = $projectMemberModel->where(['user_id' => ['IN', implode(',', $saasUserIds)], 'project_id' => $projectId])->column('id');
                $res = $projectMemberModel->where(['id' => ['IN', implode(',', $resIds)]])->delete();
            }
        }
        return $res;
    }

    /**
     * 修复实体外包状态
     * @param $filter
     * @param bool $isAsync
     * @return int
     * @throws Exception
     */
    public function fixEntityIsOutsourceStatus($filter, bool $isAsync)
    {
        $res = 0;
        $pageSize = 200;
        $p = 1;
        $defaultStartStatus = StatusService::getInstance()->getTaskDefaultStartStatus();
        $defaultDoneStatus = StatusService::getInstance()->getTaskDoneStatus();
        $generatedStatus = StatusService::getInstance()->getOne(['code' => 'task_generated'], 'id');

        $filter[] = [
            'module_id' => [
                'IN', [
                    module_by_code('design')['id'],
                    module_by_code('storyboard')['id'],
                    module_by_code('sequence')['id'],
                ],
            ],
        ];

        // 外包项目不处理是否外包状态
        $projectType = model(ProjectModel::class)->where(['id' => $filter['project_id']])->value('type');
        if ($projectType == ProjectModel::TYPE_OUTSOURCE) {
            return $res;
        }

        if (empty($generatedStatus)) {
            throw_strack_exception('status task_generated not found', ErrorCode::STATUS_IS_NOT_DEFAULT_STATUS_VALUE);
        }
        do {
            $list = model(EntityModel::class)
                ->where($filter)
                ->limit($pageSize * ($p - 1), $pageSize)
                ->select();
            $asyncIdList = [];
            foreach ($list as $row) {
                $currentModule = module_by_id($row['module_id']);
                if (empty($currentModule)) {
                    continue;
                }
                $statusField = $currentModule['code'] . "_status";
                $status = json_decode($row['json'], true)[$statusField] ?? null;
                if (!in_array($status, [$generatedStatus['id'], $defaultStartStatus['id'], $defaultDoneStatus['id']])) {
                    continue;
                }
                try {
                    if ($isAsync) {
                        $asyncIdList[] = $row['id'];
                    } else {
                        TaskExecutorTypeSyncToEntity::getInstance()->checkEntityIsOutsourceStatus($row);
                    }
                } catch (\Throwable $e) {
                    trace(format_exception_info($e), 'ERR');
                }
                $res += 1;
            }
            if ($isAsync && !empty($asyncIdList)) {
                $runInterval = 10;
                foreach (array_chunk($asyncIdList, 20) as $index => $entityIdList) {
                    Client::send('check_entity_is_outsource_status', build_queue_data_with_xu(['entity_ids' => $entityIdList]), $runInterval * $index);
                }
            }
            $p += 1;
        } while (!empty($list));
        return $res;
    }

    /**
     * 填充实体群聊
     * @param $param
     * @return int
     * @throws Exception
     */
    public function fixEntityChat($param)
    {
        $type = $param['filter']['type'];
        $projectList = model(ProjectModel::class)->column('id');
        $module = module_by_code($type);
        $entityFilter = ['module_id' => $module['id']];
        if (isset($param['filter']['entity_id'])) {
            $entityFilter['id'] = $param['filter']['entity_id'];
        }
        $total = 0;
        if ($type == 'design') {
            foreach ($projectList as $item) {
                $entityFilter['project_id'] = $item;
                $page = 1;
                while (true) {
                    // 获取每个项目下面的概念实体
                    $entityList = model(EntityModel::class)
                        ->field('id,name,project_id,tenant_id,created_by,code')
                        ->where($entityFilter)
                        ->page($page, 100)
                        ->select();
                    if (empty($entityList)) {
                        break;
                    }
                    $entityIds = array_column($entityList, 'id');
                    $page++;
                    Client::send('batch_create_entity_chat', build_queue_data_with_xu(['entity_ids' => $entityIds]));
                }
            }
        } else if ($type == 'storyboard') {
            foreach ($projectList as $item) {
                $entityFilter['project_id'] = $item;
                $data = [];
                // 获取每个项目下面的概念实体
                $entityEpisodeMap = model(EntityModel::class)
                    ->where($entityFilter)
                    ->group('initial_episode_code')
                    ->column('initial_episode_code');
                if (empty($entityEpisodeMap)) {
                    continue;
                }
                $entityEpisodeMap = array_filter($entityEpisodeMap);
                // 获取剧集的实体Code
                $episodeList = model(EntityModel::class)
                    ->field('id,code,name,project_id,tenant_id,created_by')
                    ->where(['module_id' => module_by_code('episode')['id'], 'code' => ['IN', $entityEpisodeMap], 'project_id' => $item])
                    ->select();
                Client::send('batch_create_entity_chat', build_queue_data_with_xu(['entity' => $episodeList, 'type' => 'storyboard']));
            }
        }
        return $total;
    }

    /**
     * 修正实体工序分类任务状态
     * @param $param
     * @return int
     */
    public function fixEntityStepCategoryStatus($param)
    {
        $filter = $param['filter'];
        $filter['step_type'] = 'step_category';
        $moduleList = model(ModuleModel::class)->where(['code' => ['IN', ['design', 'asset', 'sequence', 'shot', 'level']]])->select();
        $filter['entity_module_id'] = ['IN', array_column($moduleList, 'id')];
        $entityStepCategoryStepTaskQueryFields = [
            'id',
            'entity_id',
            'step_category_id',
            'step_id',
            'task_status',
        ];
        $entityStepCategoryStepTaskQueryFields = transform_custom_fields($entityStepCategoryStepTaskQueryFields, 'task');
        $entityStepCategoryStepTaskQueryFields[] = "concat(entity_id,'#',step_category_id) as group_key";
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        $startStatus = $statusService->getTaskDefaultStartStatus();
        $waitPublishStatus = $statusService->getTaskReadyToPublishStatus();
        $closedStatus = $statusService->getOne(['code' => 'closed'], '*');
        $res = 0;
        $p = 0;
        do {
            $p += 1;
            $taskList = model(TaskModel::class)
                ->where($filter)
                ->field($entityStepCategoryStepTaskQueryFields)
                ->page($p, 500)
                ->select();
            if (empty($taskList)) {
                continue;
            }

            $entityStepCategoryStepTaskListFilter = [
                0 => [
                    '_logic' => 'or',
                ],
                "_string" => "JSON_UNQUOTE(JSON_EXTRACT(json,'$.is_dismantle_task')) is null",
            ];
            $stepCategoryTaskMap = array_column($taskList, null, 'group_key');
            foreach ($taskList as $taskItem) {
                $entityStepCategoryStepTaskListFilter[0][] = [
                    'entity_id' => $taskItem['entity_id'],
                    'step_category_id' => $taskItem['step_category_id'],
                ];
            }
            $entityStepCategoryStepTaskListFilter['step_type'] = 'step';

            $entityStepCategoryStepTaskList = model(TaskModel::class)
                ->where($entityStepCategoryStepTaskListFilter)
                ->field($entityStepCategoryStepTaskQueryFields)
                ->order('entity_id,step_category_id,id')
                ->select();
            $entityStepCategoryStepTaskListGrouped = array_group_by($entityStepCategoryStepTaskList, 'group_key');

            $needUpdateData = [];
            foreach ($entityStepCategoryStepTaskListGrouped as $groupKey => $entityStepCategoryStepTaskList) {
                $currentStepCategoryTask = $stepCategoryTaskMap[$groupKey] ?? null;
                // 任务已经被废除 跳过
                if (empty($currentStepCategoryTask) || $currentStepCategoryTask['task_status'] == $closedStatus['id']) {
                    continue;
                }
                $isStart = in_array($startStatus['id'], array_column($entityStepCategoryStepTaskList, 'task_status'));
                $entityStepCategoryStepTaskList = array_column($entityStepCategoryStepTaskList, null, 'id');
                ksort($entityStepCategoryStepTaskList);
                $lastTask = array_last($entityStepCategoryStepTaskList);
                $updateData = null;
                if ($this->isAllClosed($entityStepCategoryStepTaskList, $closedStatus['id'])) {
                    $updateData = [
                        'id' => $currentStepCategoryTask['id'],
                        'task_status' => $closedStatus['id'],
                    ];
                } else if (in_array($lastTask['task_status'], [$waitPublishStatus['id'], $doneStatus['id']])) {
                    $updateData = [
                        'id' => $currentStepCategoryTask['id'],
                        'task_status' => $lastTask['task_status'],
                    ];
                } else if ($isStart) {
                    $updateData = [
                        'id' => $currentStepCategoryTask['id'],
                        'task_status' => $startStatus['id'],
                    ];
                }
                // 不需要状态变更 跳过
                if (empty($updateData) || $updateData['task_status'] == $currentStepCategoryTask['task_status']) {
                    continue;
                }
                // 修改工序分类任务状态
                $needUpdateData[$updateData['task_status']][] = $updateData['id'];
                $res += 1;
            }
            // 进行分组 批量更新
            foreach ($needUpdateData as $taskStatus => $idList) {
                $idStr = implode(',', $idList);
                model(TaskModel::class)->execute("update task set json=json_set(json,'$.task_status',{$taskStatus}) where id in ($idStr)");
            }
        } while (!empty($taskList));
        return $res;
    }

    /**
     * 修正实体工序分类任务状态
     * @return int
     */
    public function fixStepCategoryTaskPlanTime($filter)
    {
        $page = 1;
        while (true) {
            $fixStepCategoryData = model(TaskModel::class)
                ->field('id,name,step_type,step_category_id,plan_start_time,plan_end_time,entity_id')
                ->where(['step_type' => 'step_category'])
                ->where($filter)
                ->page($page, 500)->select();
            if (empty($fixStepCategoryData)) {
                break;
            }
            $page++;
            Client::send('task_step_category_plan_time_fix_data', build_queue_data_with_xu(['fixData' => $fixStepCategoryData, 'filter' => $filter]));
        }
        return $page;
    }

    /**
     * 是否全部任务关闭了
     * @param array $entityStepCategoryStepTaskList
     * @param int   $closedId
     * @return bool
     */
    private function isAllClosed(array $entityStepCategoryStepTaskList, int $closedId)
    {
        $closedCount = count(array_filter($entityStepCategoryStepTaskList, function ($i) use ($closedId) {
            return $i['task_status'] == $closedId;
        }));
        return $closedCount === count($entityStepCategoryStepTaskList);
    }

    /**
     * 修复工时记录
     * @param $param
     */
    public function fixWorkTimeRecord($param)
    {
        // 截取时间段,获取到每天的上下班的时间
        $filter = $param['filter'];
        $startTime = strtotime($filter['start_time']);
        $endTime = strtotime($filter['end_time']);
        $tenantId = $filter['tenant_id'];
        $userId = $filter['user_id'] ?? [];

        //以月份修补
        $startMonth = strtotime(date('Y-m', $startTime));
        $endMonth = strtotime("+1 month", strtotime(date('Y-m', $endTime))) - 1;

        $currentMonth = $startMonth;
        while ($currentMonth < $endMonth) {
            Client::send("fix_month_work_time_record", build_queue_data_with_xu([
                "user_id" => $userId,
                "tenant_id" => $tenantId,
                "start_time" => $currentMonth,
                "end_time" => strtotime("+1 month", $currentMonth) - 1,
            ]), 10);
            $currentMonth = strtotime("+1 month", $currentMonth);
        }
    }

    /**
     * 以人数修补工时记录
     * @param $userList
     * @param $tenantId
     * @param $startTime
     * @param $endTime
     * @return array|string
     * @throws Exception
     */
    public function fixWorkTimeRecordItem($userList, $tenantId, $startTime, $endTime)
    {
        // 每一百人发送一次
        $unionIds = [];
        $unionMap = [];
        $userIds = [];
        foreach ($userList as $userItem) {
            $unionIds[] = $userItem['union_id'];
            $userIds[] = $userItem['id'];
            $unionMap[$userItem['union_id']] = $userItem['id'];
        }
        $workTimeRecordList = model(WorkTimeRecordModel::class)->where([
            "user_id" => ["IN", $userIds],
            "tenant_id" => $tenantId,
            "record_time" => ["BETWEEN", [$startTime, $endTime]],
        ])->select();

        // 判定有没有生成过的,生成过的将不在生成
        $workTimeRecordData = [];
        foreach ($workTimeRecordList as $workTimeRecordItem) {
            $workTimeRecordData[$workTimeRecordItem["user_id"]][$workTimeRecordItem["record_time"]] = $workTimeRecordItem["record_time"];
        }

        $attendanceList = teamones_request("teamones-work-hour", "/attendance/list", [
            'param' => [
                "filter" => [
                    "im_user_id" => ["-in", join(",", $unionIds)],
                    "user_check_time" => [
                        "-bw",
                        [$startTime * 1000, $endTime * 1000],
                    ],
                    "source_type" => ["-neq", "SYSTEM"],
                    "time_result" => ["-neq", "NotSigned"],
                    "location_result" => ["-neq", "NotSigned"],
                ],
                "fields" => "check_type,work_date,user_check_time,im_user_id,ding_user_id",
            ],
        ]);

        $attendanceList = $attendanceList["data"] ?? [];

        if (empty($attendanceList)) {
            return "not find attendance";
        }

        $attendanceData = [];
        foreach ($attendanceList as $attendanceItem) {
            $workDate = $attendanceItem['work_date'] / 1000;
            $saasUserId = $unionMap[$attendanceItem["im_user_id"]];
            if (isset($workTimeRecordData[$saasUserId][$workDate])) {
                continue;
            }

            $checkType = $attendanceItem["check_type"];
            $userCheckTime = $attendanceItem["user_check_time"] / 1000;

            if (!isset($attendanceData[$saasUserId][$workDate][$checkType])) {
                $attendanceData[$saasUserId][$workDate][$checkType] = $userCheckTime;
            } elseif (
                ($checkType == "OnDuty" && $attendanceData[$saasUserId][$workDate][$checkType] > $userCheckTime) ||
                ($checkType == "OffDuty" && $attendanceData[$saasUserId][$workDate][$checkType] < $userCheckTime)) {
                $attendanceData[$saasUserId][$workDate][$checkType] = $userCheckTime;
            }

            if (!empty($attendanceItem["shift_query_rests"])) {
                foreach ($attendanceItem["shift_query_rests"] as $attendanceItemRests) {
                    if (!isset($attendanceData[$saasUserId][$workDate]["rests"][$attendanceItemRests["check_type"]])) {
                        $currentRestTime = date('Y-m-d', $workDate) . " " . date("H:i:s", strtotime($attendanceItemRests["check_time"]));
                        $attendanceData[$saasUserId][$workDate]["rests"][$attendanceItemRests["check_type"]] = $currentRestTime;
                    }
                }
            }

        }

        //去除掉缺卡的 天数
        foreach ($attendanceData as $saasUserId => $attendanceDataItem) {
            foreach ($attendanceDataItem as $attendanceWorkDate => $attendanceDataItemDay) {
                // 判定下班的与上班的是否都存在
                if (!(isset($attendanceDataItemDay["OnDuty"]) && isset($attendanceDataItemDay["OffDuty"]))) {
                    unset($attendanceData[$saasUserId][$attendanceWorkDate]);
                    continue;
                }

                //判定休息时间是否存在 , 获取上班时间段
                if (empty($attendanceDataItemDay["rests"])) {
                    $attendanceData[$saasUserId][$attendanceWorkDate]["working_hours"][] = [
                        'start_time' => $attendanceDataItemDay["OnDuty"],
                        'end_time' => $attendanceDataItemDay["OffDuty"],
                    ];
                } else {
                    $attendanceData[$saasUserId][$attendanceWorkDate]["working_hours"][] = [
                        'start_time' => $attendanceDataItemDay["OnDuty"],
                        'end_time' => $attendanceDataItemDay["rests"]["OnDuty"],
                    ];

                    $attendanceData[$saasUserId][$attendanceWorkDate]["working_hours"][] = [
                        'start_time' => $attendanceDataItemDay["rests"]["OffDuty"],
                        'end_time' => $attendanceDataItemDay["OffDuty"],
                    ];
                }

            }
        }

        if (empty($attendanceData)) {
            return "not find attendance data";
        }

        $attendanceDataUserIds = array_keys($attendanceData);

        // 查询出所有需的timelog
        $timeLogList = model(TimelogModel::class)->where([
            'created_by' => ["IN", $attendanceDataUserIds],
            'tenant_id' => $tenantId,
            'module_id' => module_by_code("task")["id"],
            'start_time' => ["BETWEEN", [$startTime, $endTime]],
        ])->field("id,link_id,tenant_id,module_id,created_by,start_time,end_time,complete")->select();

        // 只获取有打卡日期的timelog
        //计算 timelog 来生成work_time_record
        $notCompleteTimeLog = [];
        $workTimeRecordResult = [];
        $timeLogType = "standard_task";

        $taskIds = [];
        $timeLogList = array_column($timeLogList, null, "id");
        foreach ($timeLogList as $timeLogListId => $timeLogListItem) {
            if (!isset($attendanceData[$timeLogListItem['created_by']][strtotime(date('Y-m-d', $timeLogListItem['start_time']))])) {
                unset($timeLogList[$timeLogListId]);
                continue;
            }
            $taskIds[$timeLogListItem["link_id"]] = $timeLogListItem["link_id"];

            //找出未完成,将未完成的根据当天的下班结束时间结束掉
            if ($timeLogListItem["complete"] == "no") {
                $notCompleteTimeLog[] = [
                    'id' => $timeLogListItem["id"],
                    "end_time" => date('Y-m-d H:i:s', $attendanceData[$timeLogListItem['created_by']][strtotime(date('Y-m-d', $timeLogListItem['start_time']))]["OffDuty"]),
                ];
            }
        }

        if (!empty($notCompleteTimeLog)) {
            foreach ($notCompleteTimeLog as $notCompleteTimeLogItem) {
                $res = TimelogService::getInstance()->stopTimer($notCompleteTimeLogItem);
                $timeLogList[$res['id']] = $res;
            }
        }

        $taskList = [];
        if (!empty($taskIds)) {
            $taskIds = array_values($taskIds);
            $taskList = model(TaskModel::class)
                ->join("entity on entity.id = task.entity_id")
                ->where(["task.id" => ["IN", $taskIds]])->field("task.id,task.name as task_name,entity.project_id,entity.initial_episode_code,entity.showings_number,entity.name")->select();
            $taskList = array_column($taskList, null, "id");
        }

        foreach ($timeLogList as $timeLogListItem) {
            $currentDay = strtotime(date('Y-m-d', $timeLogListItem['start_time']));
            $currentAttendance = $attendanceData[$timeLogListItem['created_by']][$currentDay];

            $workingHours = $currentAttendance["working_hours"];
            $userId = $timeLogListItem["created_by"];

            $workTime = null;
            //如有超出部分 ,则截取部分. 全部超出, 将不记录
            foreach ($workingHours as $workingHoursItem) {
                if ($timeLogListItem['start_time'] < $workingHoursItem["end_time"]) {
                    $currentEndTime = $timeLogListItem['end_time'] > $workingHoursItem["end_time"] ? $workingHoursItem["end_time"] : $timeLogListItem['end_time'];
                } else {
                    continue;
                }

                if ($timeLogListItem['end_time'] > $workingHoursItem["start_time"]) {
                    $currentStartTime = $timeLogListItem['start_time'] < $workingHoursItem["start_time"] ? $workingHoursItem["start_time"] : $timeLogListItem['start_time'];
                } else {
                    continue;
                }
                if (!isset($workTime)) {
                    $workTime = $currentEndTime - $currentStartTime;
                } else {
                    $workTime += ($currentEndTime - $currentStartTime);
                }
            }

            if (!isset($workTime)) {
                continue;
            }

            $key = join("_", [$timeLogListItem['link_id'], $timeLogListItem['tenant_id'], $timeLogType, $timeLogListItem['module_id'], $userId, $currentDay]);

            $taskName = join("/", array_filter([
                $taskList[$timeLogListItem['link_id']]['initial_episode_code'] ?? "",
                $taskList[$timeLogListItem['link_id']]['showings_number'] ?? "",
                $taskList[$timeLogListItem['link_id']]['name'] ?? "",
                $taskList[$timeLogListItem['link_id']]['task_name'] ?? "",
            ], function ($taskNameItem) {
                return !empty($taskNameItem);
            }));

            if (!isset($workTimeRecordResult[$key])) {
                $workTimeRecordResult[$key] = [
                    "name" => $taskName,
                    'link_id' => $timeLogListItem['link_id'],
                    'tenant_id' => $timeLogListItem['tenant_id'],
                    'type' => $timeLogType,
                    'module_id' => $timeLogListItem['module_id'],
                    'project_id' => $taskList[$timeLogListItem['link_id']]["project_id"] ?? 0,
                    'user_id' => $userId,
                    'record_time' => $currentDay,
                    'work_time' => $workTime,
                    'created' => time(),
                    'updated' => time(),
                ];
            } else {
                $workTimeRecordResult[$key]['work_time'] = $workTimeRecordResult[$key]['work_time'] + $workTime;
            }
        }

        if (!empty($workTimeRecordResult)) {
            $workTimeRecordResult = array_values($workTimeRecordResult);
            model(WorkTimeRecordModel::class)->addAll($workTimeRecordResult);
//            $workTimeRecordResult = array_map(function ($workTimeRecordResultItem) {
            //                $workTimeRecordResultItem["record_time"] = date('Y-m-d H:i:s', $workTimeRecordResultItem["record_time"]);
            //                $workTimeRecordResultItem["work_time"] = $workTimeRecordResultItem["work_time"] / (60 * 60);
            //                return $workTimeRecordResultItem;
            //            }, $workTimeRecordResult);
        }
//        $attendanceData = array_map(function ($attendanceDataItem) {
        //            $attendanceDataItemResult = [];
        //            foreach ($attendanceDataItem as $attendanceDataDay => $attendanceDataDayItem) {
        //                $attendanceDataItemResult[date('Y-m-d H:i:s', $attendanceDataDay)] = [
        //                    "OffDuty" => date('Y-m-d H:i:s', $attendanceDataDayItem["OffDuty"]),
        //                    "OnDuty" => date('Y-m-d H:i:s', $attendanceDataDayItem["OnDuty"])
        //                ];
        //            }
        //            return $attendanceDataItemResult;
        //        }, $attendanceData);

        return [
            "attendance_data" => $attendanceData,
            "not_complete_time_log" => $notCompleteTimeLog,
            "work_time_record_result" => $workTimeRecordResult,
        ];
    }

    /**
     * 以月份修补工时记录
     * @param $userId
     * @param $tenantId
     * @param $startTime
     * @param $endTime
     * @return string
     * @throws Exception
     */
    public function fixMonthWorkTimeRecord($userId, $tenantId, $startTime, $endTime)
    {
        // 没有则修复所有用户 (查所有的只针对有timelog的)
        if (empty($userId)) {
            $userIds = model(TimelogModel::class)
                ->where([
                    'tenant_id' => $tenantId,
                    'module_id' => module_by_code("task")["id"],
                    'start_time' => ["BETWEEN", [$startTime, $endTime]],
                ])
                ->field("created_by")
                ->group("created_by")
                ->select();
            $userIds = array_column($userIds, 'created_by');
        } else {
            $userIds = $userId;
        }

        if (empty($userIds)) {
            throw new LogicException("not find user");
        }

        $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->field("id,union_id")->select();

        $number = 50;
        foreach (array_chunk($userList, $number) as $userItemList) {
            Client::send("fix_work_time_record", build_queue_data_with_xu([
                'user_list' => $userItemList, 'tenant_id' => $tenantId, 'start_time' => $startTime, 'end_time' => $endTime,
            ]));
        }
    }

    /**
     * 刷新剧集状态
     * @param $filter
     * @return int
     * @throws Exception
     */
    public function flushEpisodeStatus($filter)
    {
        $res = 0;
        $projectList = model(ProjectModel::class)->where($filter)->select();
        if (empty($projectList)) {
            return $res;
        }
        $episodeList = EpisodeService::getInstance()->selectEpisode(['project_id' => ['IN', array_column($projectList, 'id')]], 'id,name,code,project_id,json');
        $defaultDoneStatus = StatusService::getInstance()->getTaskDoneStatus();
        $defaultStartStatus = StatusService::getInstance()->getTaskDefaultStartStatus();
        $defaultStatus = StatusService::getInstance()->getTaskDefaultStatus();
        $inprogressStatusList = StatusService::getInstance()->getList(['correspond' => 'in_progress'], '*');
        $inprogressStatusIdList = array_column($inprogressStatusList, 'id');
        $inprogressStatusIds = implode(',', $inprogressStatusIdList);
        $episodeListGrouped = array_group_by($episodeList, 'project_id');
        $startedStatusList = StatusService::getInstance()->getList(['correspond' => ['IN', ['in_progress', 'daily', 'feedback', 'done']]], '*');
        $startedStatusIdList = array_column($startedStatusList, 'id');
        $startedStatusIds = implode(',', $startedStatusIdList);

        $beforeChenPianStepCategoryList = model(StepCategoryModel::class)->where(['code' => ['IN', ['lighting', 'efx']]])->select();
        $beforeChenPianStepCategoryIdList = array_column($beforeChenPianStepCategoryList, 'id');
        foreach ($projectList as $project) {
            $episodeList = $episodeListGrouped[$project['id']] ?? [];
            if (empty($episodeList)) {
                continue;
            }
            $currentProjectEpisodeCodeList = array_column($episodeList, 'code');

            // 有已开始任务的剧集Code
            $hasStartedTaskEpisodeCodeList = model(TaskModel::class)->alias('t')
                ->join("entity e on t.entity_id = e.id")
                ->where(["t.project_id" => $project['id'], 't.step_type' => 'step', 'e.initial_episode_code' => ['IN', $currentProjectEpisodeCodeList]])
                ->where("t.json ->> '$.task_status' in ({$startedStatusIds})")
                ->group('e.initial_episode_code')
                ->field("e.initial_episode_code")
                ->column('initial_episode_code');

            // 有镜头任务的剧集
            $hasShotTaskEpisodeCodeList = model(TaskModel::class)->alias('t')
                ->join("entity e on t.entity_id = e.id")
                ->where(["t.project_id" => $project['id'], 't.step_type' => 'step', 'e.initial_episode_code' => ['IN', $currentProjectEpisodeCodeList], 'e.module_id' => module_by_code('shot')['id']])
                ->group('e.initial_episode_code')
                ->field("e.initial_episode_code")
                ->column('initial_episode_code');

            // 有未完成灯光特效镜头任务的剧集
            $hasNotDoneShotTaskEpisodeCodeList = model(TaskModel::class)->alias('t')
                ->join("entity e on t.entity_id = e.id")
                ->where(["t.project_id" => $project['id'], 't.step_type' => 'step', 'e.initial_episode_code' => ['IN', $currentProjectEpisodeCodeList], 't.step_category_id' => ['IN', $beforeChenPianStepCategoryIdList]])
                ->where("t.json->>'$.task_status' <> 7")
                ->group('e.initial_episode_code')
                ->field("e.initial_episode_code")
                ->column('initial_episode_code');

            foreach ($episodeList as $episode) {
                $episodeJson = json_decode($episode['json'], true);
                if (!empty($episodeJson['episode_is_seal']) && $episodeJson['episode_is_seal'] === 'yes') {
                    // 已封版
                    if (empty($episodeJson['episode_status']) || $episodeJson['episode_status'] != $defaultDoneStatus['id']) {
                        model(EntityModel::class)->execute("update entity set json=json_set(ifnull(json,'{}'),'$.episode_status',{$defaultDoneStatus['id']}) where id = {$episode['id']}");
                        $res += 1;
                    }
                } else {
                    // 未封板
                    $wantEpisodeStatus = null;
                    if (in_array($episode['code'], $hasShotTaskEpisodeCodeList)) {
                        // 有镜头任务
                        if (in_array($episode['code'], $hasNotDoneShotTaskEpisodeCodeList)) {
                            // 有未完成的镜头灯光任务
                            if (in_array($episode['code'], $hasStartedTaskEpisodeCodeList)) {
                                // 进行中
                                $wantEpisodeStatus = $defaultStartStatus;
                            } else {
                                // 未开始
                                $wantEpisodeStatus = $defaultStatus;
                            }
                        } else {
                            // 镜头灯光特效任务都已完成
                            // 已完成
                            $wantEpisodeStatus = $defaultDoneStatus;
                        }
                    } else {
                        if (in_array($episode['code'], $hasStartedTaskEpisodeCodeList)) {
                            // 进行中
                            $wantEpisodeStatus = $defaultStartStatus;
                        } else {
                            // 未开始
                            $wantEpisodeStatus = $defaultStatus;
                        }
                    }
                    if ((empty($episodeJson['episode_status']) || $episodeJson['episode_status'] != $wantEpisodeStatus['id'])) {
                        model(EntityModel::class)->execute("update entity set json=json_set(ifnull(json,'{}'),'$.episode_status',{$wantEpisodeStatus['id']}) where id = {$episode['id']}");
                        $res += 1;
                    }
                }
            }
        }
        return $res;
    }

    /**
     * 刷新任务剧集id
     * @param $filter
     * @param $page
     * @return array
     * @throws Exception
     */
    public function flushTaskEpisodeIds($filter, $page)
    {
        // 查询当前项目初始集
        $initEpisodeData = model(EntityModel::class)
            ->where(['module_id' => module_by_code('episode')['id']])
            ->field('id,project_id,code')->select();

        $initEpisodeListMap = array_group_by($initEpisodeData, 'project_id');

        // 查询没有剧集的实体任务
        $taskList = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id")
            ->where($filter)
            ->where(['task.step_type' => ['IN', 'step,step_category']])
            ->where("entity.initial_episode_code <> ''")
            ->where(['task.episode_id' => 0])
            ->page($page[0], $page[1])
            ->field('entity.module_id,entity.initial_episode_code as episode_code,task.id as task_id,task.name,task.step_type,task.episode_id,task.entity_id,task.project_id')
            ->select();
        if (empty($taskList)) {
            return [];
        }
        $episodeModuleId = module_by_code('episode')['id'];
        $taskListGrouped = array_group_by($taskList, 'project_id');
        $taskUpdateDataGroupedByEpisodeId = [];
        foreach ($taskListGrouped as $projectId => $taskList) {
            $episodeMap = $initEpisodeListMap[$projectId] ?? [];
            $episodeMap = array_column($episodeMap, 'id', 'code');
            if (empty($episodeMap)) {
                continue;
            }

            foreach ($taskList as $task) {
                if ($task['module_id'] == $episodeModuleId) {
                    $taskUpdateDataGroupedByEpisodeId[$task['entity_id']][] = $task['task_id'];
                    continue;
                }
                if (!isset($episodeMap[$task['episode_code']])) {
                    continue;
                }
                $taskUpdateDataGroupedByEpisodeId[$episodeMap[$task['episode_code']]][] = $task['task_id'];
            }
        }
        if (!empty($taskUpdateDataGroupedByEpisodeId)) {
            foreach ($taskUpdateDataGroupedByEpisodeId as $episodeId => $taskIdList) {
                $taskIds = implode(',', $taskIdList);
                model(TaskModel::class)->execute("update task set episode_id = {$episodeId} where id in ({$taskIds})");
            }
        }

        return $taskUpdateDataGroupedByEpisodeId;
    }

    /**
     * 补齐计划代办任务以及标签
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function fixPlanTodoTaskIdAndTag()
    {
        $planList = model(PlanModel::class)->where([
            'entity_id' => ['GT', 0],
            'type' => PlanModel::TYPE_STEP_CATEGORY,
            'task_id' => 0,
        ])->select();

        $result = [];
        if (!empty($planList)) {
            $stepCategoryList = model(StepCategoryModel::class)->select();
            $stepCategoryList = array_column($stepCategoryList, null, 'id');
            foreach (array_chunk($planList, 20) as $planArray) {
                Client::send("fix_plan_to_do_task", build_queue_data_with_xu([
                    "plan" => $planArray,
                    'step_category' => $stepCategoryList,
                ]));
            }
        }

        $todoPlanList = model(PlanModel::class)
            ->where([
                'entity_id' => ['GT', 0],
                'type' => PlanModel::TYPE_TO_DO,
            ])->select();

        if (empty($todoPlanList)) {
            return $result;
        }

        $taskIds = model(TaskModel::class)
            ->join("task_relation_tag on task.id = task_relation_tag.task_id", "LEFT")
            ->where([
                "task.id" => ["IN", array_column($todoPlanList, 'task_id')],
                "_string" => "task_relation_tag.id is null",
            ])
            ->field("task.id,task.created_by,task.created")
            ->select();

        if (!empty($taskIds)) {
            $planTaskMap = array_column($todoPlanList, null, 'task_id');
            $taskList = array_column($taskIds, null, 'id');
            $addTaskRelationTag = [];

            foreach ($taskList as $taskIdItem) {
                $addTaskRelationTag[] = [
                    'task_id' => $taskIdItem['id'],
                    'link_module_code' => 'episode',
                    'link_id' => $planTaskMap[$taskIdItem['id']]['entity_id'],
                    'tenant_id' => $planTaskMap[$taskIdItem['id']]['tenant_id'],
                    'created_by' => $taskIdItem['created_by'],
                    'created' => $taskIdItem['created'],
                    'updated' => $taskIdItem['created'],
                ];

                $addTaskRelationTag[] = [
                    'task_id' => $taskIdItem['id'],
                    'link_module_code' => 'step_category',
                    'link_id' => $planTaskMap[$taskIdItem['id']]['step_category_id'],
                    'tenant_id' => $planTaskMap[$taskIdItem['id']]['tenant_id'],
                    'created_by' => $taskIdItem['created_by'],
                    'created' => $taskIdItem['created'],
                    'updated' => $taskIdItem['created'],
                ];
            }

            if (!empty($addTaskRelationTag)) {
                $result['add_task_relation_tag'] = $addTaskRelationTag;
                model(TaskRelationTagModel::class)->addAll($addTaskRelationTag);
            }
        }

        return $result;
    }

    /**
     * 修正实体工序分类任务状态
     * @param $param
     * @return int
     */
    public function fixEntityStepCategoryStartEndTime($param)
    {
        $filter = $param['filter'];
        $filter['step_type'] = 'step_category';
        $moduleList = model(ModuleModel::class)->where(['code' => ['IN', ['design', 'asset', 'sequence', 'shot', 'level']]])->select();
//        $moduleList = model(ModuleModel::class)->where(['code' => ['IN', ['asset']]])->select();
        $filter['entity_module_id'] = ['IN', array_column($moduleList, 'id')];
        $entityStepCategoryStepTaskQueryFields = [
            'task.id',
            'task.entity_id',
            'task.step_category_id',
            'task.step_id',
            'task.start_time',
            'task.end_time',
            'task_status',
        ];
        $entityStepCategoryStepTaskQueryFields = transform_custom_fields($entityStepCategoryStepTaskQueryFields, 'task');
        $entityStepCategoryStepTaskQueryFields[] = "concat(task.entity_id,'#',task.step_category_id) as group_key";

        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        $startStatus = $statusService->getTaskDefaultStartStatus();
        $waitPublishStatus = $statusService->getTaskReadyToPublishStatus();
        $closedStatus = $statusService->getOne(['code' => 'closed'], '*');
        $res = 0;
        $p = 0;
        do {
            $p += 1;
            $taskList = model(TaskModel::class)
                ->where($filter)
                ->field($entityStepCategoryStepTaskQueryFields)
                ->page($p, 500)
                ->select();
            if (empty($taskList)) {
                continue;
            }

            $entityStepCategoryStepTaskListFilter = [
                0 => [
                    '_logic' => 'or',
                ],
                "_string" => "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.is_dismantle_task')) is null",
            ];
            $stepCategoryTaskMap = array_column($taskList, null, 'group_key');
            foreach ($taskList as $taskItem) {
                $entityStepCategoryStepTaskListFilter[0][] = [
                    'task.entity_id' => $taskItem['entity_id'],
                    'task.step_category_id' => $taskItem['step_category_id'],
                ];
            }
            $entityStepCategoryStepTaskListFilter['task.step_type'] = 'step';

            $tq = $entityStepCategoryStepTaskQueryFields;
            $tq[] = 'max_version.link_id';
            $tq[] = 'max_version.v_start_time';
            $tq[] = 'max_version.v_end_time';
            $entityStepCategoryStepTaskList = model(TaskModel::class)
                ->join("(select max(created) v_end_time,min(created) v_start_time,link_id from version where is_publish = 'yes' group by link_id) max_version on max_version.link_id = task.id", 'LEFT')
                ->where($entityStepCategoryStepTaskListFilter)
                ->field($tq)
                ->order('task.entity_id,task.step_category_id,task.id')
                ->select();
            $entityStepCategoryStepTaskListGrouped = array_group_by($entityStepCategoryStepTaskList, 'group_key');

            $needUpdateData = [];
            foreach ($entityStepCategoryStepTaskListGrouped as $groupKey => $entityStepCategoryStepTaskList) {
                $currentStepCategoryTask = $stepCategoryTaskMap[$groupKey] ?? null;
                // 任务已经被废除 跳过
                if (empty($currentStepCategoryTask) || $currentStepCategoryTask['task_status'] == $closedStatus['id']) {
                    continue;
                }

                $startTimeList = array_filter(array_column($entityStepCategoryStepTaskList, 'v_start_time'));
                $endTimeList = array_filter(array_column($entityStepCategoryStepTaskList, 'v_end_time'));
                $minStartTime = !empty($startTimeList) ? min($startTimeList) : 0;
                $maxEndTime = !empty($endTimeList) ? max($endTimeList) : 0;

                $isStart = in_array($startStatus['id'], array_column($entityStepCategoryStepTaskList, 'task_status'));
                $entityStepCategoryStepTaskList = array_column($entityStepCategoryStepTaskList, null, 'id');
                ksort($entityStepCategoryStepTaskList);
                $lastTask = array_last($entityStepCategoryStepTaskList);
                $updateData = null;
                if ($lastTask['v_end_time'] > 0 && ($currentStepCategoryTask['start_time'] != $minStartTime or $currentStepCategoryTask['end_time'] != $maxEndTime)) {
                    $updateData = [
                        'id' => $currentStepCategoryTask['id'],
                        'start_time' => $minStartTime,
                        'end_time' => $maxEndTime,
                    ];
                } else if ($minStartTime > 0 && $currentStepCategoryTask['start_time'] != $minStartTime) {
                    $updateData = [
                        'id' => $currentStepCategoryTask['id'],
                        'start_time' => $minStartTime,
                    ];
                }
                // 不需要状态变更 跳过
                if (empty($updateData)) {
                    continue;
                }
                // 修改工序分类任务状态
                $needUpdateData[] = $updateData;
                $res += 1;
            }
            foreach ($needUpdateData as $updateDataItem) {
                CommonService::instance('task')->update($updateDataItem, 'task', false);
            }

            $batchNumber = \request()->getBatchNumber();
            if (!empty($batchNumber) && Redis::lLen($batchNumber)) {
                Redis::del($batchNumber);
            }

        } while (!empty($taskList));
        return $res;
    }

    /**
     * 修复工序任务成员信息
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function fixStepTaskMember($param)
    {
        $taskList = model(TaskModel::class)
            ->join('task_member on task_member.task_id = task.id', 'LEFT')
            ->where($param['filter'])
            ->where(['task.step_type' => 'step', 'task.executor' => ['GT', 0], 'task.executor_type' => ['IN', ['inside', 'default']]])
            ->where("task_member.id is null")
            ->page($param['page'][0], $param['page'][1])
            ->field("task.id,task.executor,task.json->>'$.task_status' as task_status")
            ->select();
        $taskMemberList = [];
        foreach ($taskList as $task) {
            $taskMemberList[] = [
                'task_id' => $task['id'],
                'user_id' => $task['executor'],
                'partner' => $task['executor'],
                'created' => time(),
                'updated' => 0,
                'status' => $task['task_status'],
            ];
        }
        foreach (array_chunk($taskMemberList, 200) as $taskMemberListChunk) {
            model(TaskMemberModel::class)->addAll($taskMemberListChunk);
        }
        return $taskMemberList;
    }

    /**
     * 修复剧集关联实体
     * @param $param
     * @return array|false|mixed|string
     * @throws \Exception
     */
    public function fixEpisodeRelationEntity($param)
    {
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $entityList = model(EntityModel::class)
            ->where(["_string" => "id not in (select entity.id from entity left join entity_episode_relation on entity.id = entity_episode_relation.entity_id left join entity episode on entity_episode_relation.episode_id = episode.id where entity.project_id = episode.project_id and entity.initial_episode_code = episode.`code`) and entity.initial_episode_code !='' and showings_number !=''"])
            ->field("id as entity_id,module_id,project_id,initial_episode_code,showings_number")
            ->page(...$page)
            ->select();

        if (empty($entityList)) {
            return [];
        }

        $episodeFilter = [];
        foreach ($entityList as $entityItem) {
            $episodeFilter[$entityItem['project_id']][] = $entityItem['initial_episode_code'];
        }

        $episodeWhere = [];
        foreach ($episodeFilter as $episodeProjectIds => $episodeCodes) {
            $episodeWhere[] = [
                'project_id' => $episodeProjectIds,
                'code' => ["IN", $episodeCodes],
            ];
        }

        $episodeWhere['_logic'] = "OR";

        $episodeList = model(EntityModel::class)->where($episodeWhere)->field("id,project_id,code")->select();

        $episodeMap = [];
        foreach ($episodeList as $episodeItem) {
            $episodeMap[$episodeItem['project_id']][$episodeItem['code']] = $episodeItem;
        }

        foreach ($entityList as $entityIndex => $entityItem) {
            if (!isset($episodeMap[$entityItem['project_id']][$entityItem['initial_episode_code']])) {
                unset($entityList[$entityIndex]);
                continue;
            }
            $entityItem['episode_id'] = $episodeMap[$entityItem['project_id']][$entityItem['initial_episode_code']]['id'];
            $entityItem['entity_module_id'] = $entityItem['module_id'];
            $entityItem['created'] = time();
            unset($entityItem['module_id'], $entityItem['initial_episode_code']);
            $entityList[$entityIndex] = $entityItem;
        }

        if (empty($entityList)) {
            return [];
        }
        $entityList = array_values($entityList);
        model(EntityEpisodeRelationModel::class)->addAll($entityList);
        return $entityList;
    }

    /**
     * 拆分多分类排期
     */
    public function fixSplitMultiStepCategoryPlan()
    {

    }

    /**
     * 修复项目成员记录重复问题
     * @param $where
     * @return array
     * @throws Exception
     */
    public function fixProjectMemberDuplicateData($where)
    {
        $option = OptionsService::getInstance()->getOptionsData('project_initiation');
        $showProjectShowStepCategoryList = ProjectService::getInstance()->getShowStepCategoryGroup();
        $showProjectShowStepCategoryMap = [];
        foreach ($showProjectShowStepCategoryList as $showProjectShowStepCategoryItem) {
            sort($showProjectShowStepCategoryItem['children_ids']);
            $showProjectShowStepCategoryMap[] = implode(',', $showProjectShowStepCategoryItem['children_ids']);
        }
        $roleList = [];

        if (!empty($option['manage'])) {
            $roleList['manage'] = teamones_request('teamones-im', 'role/select', [
                'param' => [
                    'filter' => [
                        'role.id' => ['-in', $option['manage']],
                    ],
                ],
            ])['data']['rows'];
        }

        if (!empty($option['production'])) {
            $roleList['production'] = teamones_request('teamones-im', 'role/select', [
                'param' => [
                    'filter' => [
                        'role.id' => ['-in', $option['production']],
                    ],
                ],
            ])['data']['rows'];
        }

        $res = [];
        $list = model(ProjectMemberModel::class)->where($where)->select();
        $listGroupByType = array_group_by($list, 'type');
        foreach ($listGroupByType as $type => $list) {
            $roleList = $roleList[$type] ?? [];
            $roleCodeList = array_column($roleList, 'code');
            $roleUserMapList = [];
            $projectStepCategoryUserMap = [];
            $needDelete = [];
            foreach ($list as $item) {
                $roleCode = $item['role_code'] ?? '';
                /**
                 *
                 * 1. 角色组的用户判断
                 *
                 * 2. 环节组的用户判断
                 *
                 */

                // 角色组判断
                if (!empty($roleCode) && in_array($roleCode, $roleCodeList)) {
                    if (empty($roleUserMapList[$roleCode][$item['user_id']])) {
                        $roleUserMapList[$roleCode][$item['user_id']] = $item['user_id'];
                    } else {
                        $needDelete[] = $item['id'];
                    }
                    continue;
                }
                // 环节组判断
                if (!empty($item['step_category_ids']) && in_array($item['step_category_ids'], $showProjectShowStepCategoryMap)) {
                    if (empty($projectStepCategoryUserMap[$item['step_category_ids']][$item['user_id']])) {
                        $projectStepCategoryUserMap[$item['step_category_ids']][$item['user_id']] = $item['user_id'];
                    } else {
                        $needDelete[] = $item['id'];
                    }
                    continue;
                }
                // 判断是否存在于当前组内
                $stepCategoryIds = $showProjectShowStepCategoryMap[0];
                if (empty($projectStepCategoryUserMap[$stepCategoryIds][$item['user_id']])) {
                    $item['step_category_ids'] = $stepCategoryIds;
                    $res['updated'][] = model(ProjectMemberModel::class)->modifyItem($item);
                    $projectStepCategoryUserMap[$stepCategoryIds][$item['user_id']] = $item['user_id'];
                } else {
                    $needDelete[] = $item['id'];
                }
            }

            if (!empty($needDelete)) {
                model(ProjectMemberModel::class)->where(['id' => ['IN', implode(',', $needDelete)]])->delete();
            }
            $res['deleted'] = $needDelete;
        }
        return $res;
    }

    /**
     * 填充历史数据的交付时间
     * @param $filter
     * @param $page
     * @return array
     */
    public function fillDefaultPlanDeliveryTime($filter, $page)
    {
        $taskList = model(TaskModel::class)
            ->where($filter)
            ->page(...$page)
            ->select();
        $taskIdList = array_column($taskList, 'id');
        $taskRelationTagList = model(TaskRelationTagModel::class)
            ->where(['task_id' => ['IN', implode(',', $taskIdList)]])
            ->where(['link_module_code' => ['IN', ['step_category', 'show_step_category', 'episode']]])
            ->field('task_id,link_id,link_module_code')
            ->select();
        $episodeIdList = [];
        $stepCategoryIdList = [];
        $taskRelationFormat = [];
        foreach ($taskRelationTagList as $taskRelationTag) {
            if (in_array($taskRelationTag['link_module_code'], ['step_category', 'show_step_category'])) {
                $stepCategoryIdList[$taskRelationTag['link_id']] = $taskRelationTag['link_id'];
                $taskRelationFormat[$taskRelationTag['task_id']]['step_category'][$taskRelationTag['link_id']] = $taskRelationTag['link_id'];
            } elseif ($taskRelationTag['link_module_code'] === 'episode') {
                $episodeIdList[$taskRelationTag['link_id']] = $taskRelationTag['link_id'];
                $taskRelationFormat[$taskRelationTag['task_id']]['episode'][$taskRelationTag['link_id']] = $taskRelationTag['link_id'];
            }
        }
        $planTimeMap = PlanService::getInstance()->getStepCategoryPlanEndTime($episodeIdList, $stepCategoryIdList);
        $taskUpdateData = [];
        foreach ($taskList as $task) {
            $currentTaskRelationTagList = $taskRelationFormat[$task['id']] ?? [];
            if (empty($currentTaskRelationTagList)
                || empty($currentTaskRelationTagList['step_category'])
                || empty($currentTaskRelationTagList['episode'])
            ) {
                continue;
            }
            $maxDeliveryTime = 0;
            foreach ($currentTaskRelationTagList['episode'] as $episodeId) {
                foreach ($currentTaskRelationTagList['step_category'] as $stepCategoryId) {
                    if (empty($planTimeMap[$episodeId][$stepCategoryId])) {
                        continue;
                    }
                    if ($planTimeMap[$episodeId][$stepCategoryId] <= $maxDeliveryTime) {
                        continue;
                    }
                    $maxDeliveryTime = $planTimeMap[$episodeId][$stepCategoryId];
                }
            }
            if ($task['plan_delivery_time'] == $maxDeliveryTime) {
                continue;
            }
            $taskUpdateData[] = [
                'id' => $task['id'],
                'plan_delivery_time' => $maxDeliveryTime,
            ];
        }
        if (!empty($taskUpdateData)) {
            model(TaskModel::class)->saveAll($taskUpdateData);
        }
        return $taskUpdateData;
    }

    /**
     * 修改渲染工序到灯光环节
     * @param $filter
     * @param $page
     * @return array
     * @throws \Exception
     */
    public function changeRenderToLighting($filter, $page)
    {
        $res = [
            'task_changed' => 0,
            'step_changed' => 0,
            'step_workflow_changed' => 0,
        ];
        // 修改渲染工序到灯光
        $renderStepCodeList = ['engine_render', 'other_render'];
        $renderStepList = model(StepModel::class)->where(['code' => ['IN', $renderStepCodeList]])->select();
        $renderStepCategory = model(StepCategoryModel::class)->where(['code' => 'Rendering'])->find();
        $lightingStepCategory = model(StepCategoryModel::class)->where(['code' => 'lighting'])->find();
        $lightingParentStepCategory = model(StepCategoryModel::class)->where(['id' => $lightingStepCategory['parent_id']])->find();
        foreach ($renderStepList as $step) {
            if ($step['step_category_id'] == $lightingStepCategory['id']) {
                continue;
            }
            model(StepModel::class)->save(['id' => $step['id'], 'step_category_id' => $lightingStepCategory['id']]);
            $res['step_changed'] += 1;
        }

        // 修改工序流里的环节到灯光
        $stepWorkflowList = model(StepWorkflowModel::class)->select();

        foreach ($stepWorkflowList as $stepWorkflow) {
            $config = json_decode($stepWorkflow['config'], true);
            $changed = false;
            foreach ($config['nodes'] as &$nodeList) {
                foreach ($nodeList as &$node) {
                    if (in_array($node['module_code'], $renderStepCodeList) &&
                        ($node['step_category_id'] != $lightingStepCategory['id']
                            ||
                            $node['step_category_parent_id'] != $lightingParentStepCategory['id'])
                    ) {
                        $node['step_category_id'] = $lightingStepCategory['id'];
                        $node['step_category_parent_id'] = $lightingParentStepCategory['id'];
                        $changed = true;
                    }
                }
            }

            foreach ($config['stepList'] as &$step) {
                if (in_array($step['code'], $renderStepCodeList)
                    && (
                        $step['step_category_id'] != $lightingStepCategory['id']
                        || $step['step_category_name'] != $lightingStepCategory['name']
                        || $step['step_category_parent_id'] != $lightingParentStepCategory['id']
                        || $step['step_category_parent_name'] != $lightingParentStepCategory['name']
                    )
                ) {
                    $step['step_category_id'] = $lightingStepCategory['id'];
                    $step['step_category_name'] = $lightingStepCategory['name'];
                    $step['step_category_parent_id'] = $lightingParentStepCategory['id'];
                    $step['step_category_parent_name'] = $lightingParentStepCategory['name'];
                    $changed = true;
                }
            }

            if ($changed) {
                model(StepWorkflowModel::class)->save(['id' => $stepWorkflow['id'], 'config' => json_encode($config)]);
                $res['step_workflow_changed'] += 1;
            }
        }

        // 渲染的工序任务刷为灯光下的任务

        while (1) {
            $taskList = model(TaskModel::class)
                ->where([
                    'step_id' => ['IN', array_column($renderStepList, 'id')],
                    'step_category_id' => $renderStepCategory['id'],
                ])
                ->where($filter)
                ->page(...$page)
                ->field('id,step_id,step_category_id')
                ->select();
            if (empty($taskList)) {
                break;
            }
            $updateToLightingTaskIdList = [];
            foreach ($taskList as $task) {
                if ($task['step_category_id'] === $lightingStepCategory['id']) {
                    continue;
                }
                $updateToLightingTaskIdList[] = $task['id'];
                $res['task_changed'] += 1;
            }

            if (!empty($updateToLightingTaskIdList)) {
                model(TaskModel::class)
                    ->where(['id' => ['IN', implode(',', $updateToLightingTaskIdList)]])
                    ->save(['step_category_id' => $lightingStepCategory['id']]);
            }
        }

        // 渲染下的渲染子任务调整为灯光的子任务
        $renderSubStep = model(StepModel::class)->where(['step_category_id' => $renderStepCategory['id'], 'is_dismantle_step' => 'yes'])->find();
        $lightSubStep = model(StepModel::class)->where(['step_category_id' => $lightingStepCategory['id'], 'is_dismantle_step' => 'yes'])->find();
        while (1) {
            $taskList = model(TaskModel::class)
                ->where([
                    'step_id' => $renderSubStep['id'],
                    'step_category_id' => $renderStepCategory['id'],
                ])
                ->where($filter)
                ->page(...$page)
                ->field('id,step_id,step_category_id')
                ->select();
            if (empty($taskList)) {
                break;
            }
            $updateToLightingSubTaskIdList = [];
            foreach ($taskList as $task) {
                $res['task_changed'] += 1;
                $updateToLightingSubTaskIdList[] = $task['id'];
            }
            if (!empty($updateToLightingSubTaskIdList)) {
                model(TaskModel::class)
                    ->where(['id' => ['IN', implode(',', $updateToLightingSubTaskIdList)]])
                    ->save([
                        'step_category_id' => $lightingStepCategory['id'],
                        'step_id' => $lightSubStep['id'],
                    ]);
            }
        }
        return $res;
    }

    /**
     * 填充历史延期单环节确认数据
     * @param $filter
     * @param $page
     * @return array
     * @throws \Exception
     */
    public function fillDelayOrderStepCategoryConfirm($filter, $page)
    {
        $res = [];
        $delayOrderIdList = model(DelayOrderModel::class)
            ->alias('do')
            ->join("delay_order_task dot on do.id = dot.delay_order_id")
            ->join('delay_order_task_step_category_confirm dotscc on dot.id = dotscc.delay_order_task_id', 'LEFT')
            ->where(['dotscc.id' => ['EXP', 'IS NULL']])
            ->where($filter)
            ->field('do.id')
            ->group('do.id')
            ->page($page[0], $page[1])
            ->column('id');

        if (empty($delayOrderIdList)) {
            return $res;
        }

        $delayOrderTaskList = model(DelayOrderTaskModel::class)->where(['delay_order_id' => ['IN', $delayOrderIdList]])->select();
        if (empty($delayOrderTaskList)) {
            // 找不到对应的延期单任务
            return $res;
        }
        $delayOrderTaskIdList = array_column($delayOrderTaskList, 'id');
        $delayOrderIdList = array_column($delayOrderTaskList, 'delay_order_id', 'delay_order_id');
        $delayOrderList = model(DelayOrderModel::class)->where(['id' => ['IN', $delayOrderIdList]])->select();

        $reviewRequestList = model(ReviewRequestModel::class)
            ->where(['module_id' => module_by_code('delay_order_task')['id']])
            ->where(['link_id' => ['IN', $delayOrderTaskIdList]])
            ->select();

        $reviewRequestNodeList = model(ReviewRequestNodeModel::class)
            ->where(['review_request_id' => ['IN', implode(',', array_column($reviewRequestList, 'id'))]])
            ->select();
        $reviewRequestNodeList = array_group_by($reviewRequestNodeList, 'review_request_id');
        $reviewRequestList = array_column($reviewRequestList, null, 'link_id');

        $stepCategoryConfirmAdd = [];
        $delayOrderTaskListGrouped = array_group_by($delayOrderTaskList, 'delay_order_id');
        $delayOrderList = array_column($delayOrderList, null, 'id');
        $allReviewedList = [];
        $delayOrderTaskConfirmedYes = [];
        $delayOrderTaskConfirmedNo = [];
        $delayOrderTaskEpisodeStepCategoryUpdateMap = [];
        foreach ($delayOrderTaskListGrouped as $delayOrderId => $delayOrderTaskList) {
            $delayOrder = $delayOrderList[$delayOrderId] ?? null;
            if (empty($delayOrder)) {
                continue;
            }
            $allReviewed = true;
            foreach ($delayOrderTaskList as $delayOrderTask) {
                if (empty($delayOrderTask['step_category_id']) || empty($delayOrderTask['episode_id'])) {
                    $delayOrderTaskEpisodeStepCategoryUpdateMap[$delayOrder['episode_id']][$delayOrder['step_category_id']][] = $delayOrderTask['id'];
                }
                $reviewRequest = $reviewRequestList[$delayOrderTask['id']] ?? null;
                $tempStepCategoryConfirm = [
                    'uuid' => create_uuid(),
                    'delay_order_id' => $delayOrder['id'],
                    'delay_order_task_id' => $delayOrderTask['id'],
                    'step_category_id' => $delayOrder['step_category_id'],
                    'confirm_user_id' => 0,
                    'confirm_at' => 0,
                    'is_active' => 'no',
                    'is_confirmed' => 'yes',
                    'is_pass' => 'yes',
                    'is_self_step_category' => 'yes',
                    'need_delay' => 'yes',
                    'delay_to' => 0,
                    'delay_reason' => '',
                    'created' => time(),
                    'created_by' => fill_created_by(),
                ];

                // 没有审核记录
                if (empty($reviewRequest)) {
                    $delayOrderTaskConfirmedYes[] = $delayOrderTask['id'];
                    $stepCategoryConfirmAdd[] = $tempStepCategoryConfirm;
                    continue;
                }
                $currentReviewRequestNodeList = $reviewRequestNodeList[$reviewRequest['id']] ?? [];
                if (empty($currentReviewRequestNodeList)) {
                    // 没有审核记录 一律通过
                    $delayOrderTaskConfirmedYes[] = $delayOrderTask['id'];
                    $stepCategoryConfirmAdd[] = $tempStepCategoryConfirm;
                    continue;
                }

                if ($reviewRequest['is_reviewed'] === 'yes') {
                    $lastReviewRequestNode = end($currentReviewRequestNodeList);
                    // 审核结束 填充审核结果
                    if ($reviewRequest === 'yes') {
                        $delayOrderTaskConfirmedYes[] = $delayOrderTask['id'];
                    } else {
                        $delayOrderTaskConfirmedNo[] = $delayOrderTask['id'];
                    }
                    // 设置延期审核结果
                    $tempStepCategoryConfirm['is_pass'] = $reviewRequest['is_pass'];
                    $tempStepCategoryConfirm['confirm_at'] = $reviewRequest['reviewed'];
                    $tempStepCategoryConfirm['confirm_user_id'] = $lastReviewRequestNode['plan_reviewer'];
                    $tempStepCategoryConfirm['need_delay'] = 'yes';
                    $tempStepCategoryConfirm['delay_to'] = $delayOrderTask['delay_time'];
                    $tempStepCategoryConfirm['delay_reason'] = $delayOrderTask['delay_reason'];
                } else {
                    $allReviewed = false;
                    $tempStepCategoryConfirm['is_pass'] = 'no';
                    $tempStepCategoryConfirm['confirm_at'] = 0;
                    $tempStepCategoryConfirm['confirm_user_id'] = 0;
                    $tempStepCategoryConfirm['delay_to'] = 0;
                    $tempStepCategoryConfirm['delay_reason'] = '';
                    $tempStepCategoryConfirm['is_confirmed'] = 'no';
                    $tempStepCategoryConfirm['is_active'] = 'yes';
                    $tempStepCategoryConfirm['need_delay'] = 'no';
                }
                $stepCategoryConfirmAdd[] = $tempStepCategoryConfirm;
            }

            if ($allReviewed && $delayOrder['is_reviewed'] != 'yes') {
                $allReviewedList[] = $delayOrderId;
            }
        }

        if (!empty($allReviewedList)) {
            $res['delay_order_update'] = model(DelayOrderModel::class)
                ->where(['id' => ['IN', $allReviewedList]])
                ->save([
                    'is_reviewed' => 'yes',
                ]);
        }
        if (!empty($delayOrderTaskConfirmedYes)) {
            $res['delay_order_task_update'][] = model(DelayOrderTaskModel::class)
                ->where(['id' => ['IN', $delayOrderTaskConfirmedYes]])
                ->save([
                    'step_category_confirmed' => 'yes',
                    'manager_confirmed' => 'yes',
                ]);
        }
        if (!empty($delayOrderTaskConfirmedNo)) {
            $res['delay_order_task_update'][] = model(DelayOrderTaskModel::class)
                ->where(['id' => ['IN', $delayOrderTaskConfirmedNo]])
                ->save([
                    'step_category_confirmed' => 'no',
                    'manager_confirmed' => 'no',
                ]);
        }

        foreach ($delayOrderTaskEpisodeStepCategoryUpdateMap as $episodeId => $delayOrderTaskStepCategoryGrouped) {
            foreach ($delayOrderTaskStepCategoryGrouped as $stepCategoryId => $delayOrderTaskIdList) {
                $res['delay_order_task_update'][] = model(DelayOrderTaskModel::class)
                    ->where(['id' => ['IN', $delayOrderTaskIdList]])
                    ->save([
                        'step_category_id' => $stepCategoryId,
                        'episode_id' => $episodeId,
                    ]);
            }
        }

        if (!empty($stepCategoryConfirmAdd)) {
            foreach (array_chunk($stepCategoryConfirmAdd, 200) as $stepCategoryConfirmAddChunk) {
                $res['delay_order_task_step_category_confirm_added'][] = model(DelayOrderTaskStepCategoryConfirmModel::class)
                    ->addAll($stepCategoryConfirmAddChunk);
            }
        }
        return $res;

    }

    /**
     * 修复需求的计划交付时间
     * @return array
     */
    public function fixDemandOrderPlanDeliveryTime()
    {
        $reviewFeedbackList = model(ReviewFeedbackModel::class)->where([
            "json_unquote(json_extract(json,'$.plan_delivery_time'))" => ["EXP", "IS NOT NULL"]
        ])->field("id,json,project_id,episode_codes")->select();

        //查询剧集
        $projectEpisodeCodes = [];
        foreach ($reviewFeedbackList as $reviewFeedbackListItem) {
            foreach (explode(",", $reviewFeedbackListItem['episode_codes']) as $episodeCode) {
                $projectEpisodeCodes[$reviewFeedbackListItem['project_id']][$episodeCode] = $episodeCode;
            }
        }

        $episodeCodeFilter = [];
        foreach ($projectEpisodeCodes as $projectId => $episodeCodeList) {
            $episodeCodeFilter[] = [
                'project_id' => $projectId,
                'code' => ["IN", $episodeCodeList]
            ];
        }
        $episodeCodeFilter["_logic"] = "OR";


        $episodeList = model(EntityModel::class)->where([
            [$episodeCodeFilter],
            "module_id" => module_by_code("episode")['id']
        ])->field("id,project_id,code")->select();

        $projectEpisodeCodeMap = [];
        foreach ($episodeList as $episodeListItem) {
            $projectEpisodeCodeMap[$episodeListItem['project_id']][$episodeListItem['code']] = $episodeListItem;
        }

        $updateData = [];
        foreach ($reviewFeedbackList as $reviewFeedbackListItem) {

            $reviewFeedbackJson = json_decode($reviewFeedbackListItem['json'], true);

            if (empty($reviewFeedbackJson['plan_delivery_time'])) {
                continue;
            }

            $oldPlanDeliveryTime = $reviewFeedbackJson['plan_delivery_time'];
            $isUpdate = false;
            //判定key是否大于1000,如果有则证明改过了
            foreach ($oldPlanDeliveryTime as $oldPlanDeliveryTimeKey => $oldPlanDeliveryTimeItem) {
                if ($oldPlanDeliveryTimeKey > 1000) {
                    $isUpdate = true;
                }
            }

            if ($isUpdate) {
                continue;
            }

            $tmp = [];
            foreach (explode(",", $reviewFeedbackListItem['episode_codes']) as $episodeCode) {
                if (isset($projectEpisodeCodeMap[$reviewFeedbackListItem['project_id']][$episodeCode]['id'])) {
                    $tmp[$projectEpisodeCodeMap[$reviewFeedbackListItem['project_id']][$episodeCode]['id']] = $oldPlanDeliveryTime;
                }
            }
            $reviewFeedbackJson['plan_delivery_time'] = $tmp;
            $updateData[] = [
                'id' => $reviewFeedbackListItem['id'],
                'json' => json_encode($reviewFeedbackJson)
            ];
        }

        if (!empty($updateData)) {
            model(ReviewFeedbackModel::class)->saveAll($updateData);
        }

        return $updateData;

    }

    /**
     * 修复需求单子级计划交付任务
     * @param $stepCategoryId
     * @return array
     */
    public function fixDemandOrderChildrenStepCategoryPlanDeliveryTime($stepCategoryId)
    {

        $childrenStepCategoryIds = ReviewFeedbackService::getInstance()->getDemandOrderChildrenStepCategoryId('asset', $stepCategoryId);


        //查找没有完成得得review_feedback
        $reviewFeedbackList = model(ReviewFeedbackModel::class)->where([
            'module_code' => 'asset',
            'status' => ["IN", [ReviewFeedbackModel::STATUS_NOT_STARTED, ReviewFeedbackModel::STATUS_IN_PROGRESS]],
            "json_unquote(json_extract(json,'$.plan_delivery_time'))" => ["EXP", "is not null"]
        ])->select();

        //查找没有发布过得review_feedback_entity
        $reviewFeedbackEntityList = model(ReviewFeedbackEntityModel::class)->where([
            "published" => ReviewFeedbackEntityModel::PUBLISHED_NO,
            "json_unquote(json_extract(json,'$.plan_delivery_time'))" => ["EXP", "is not null"]
        ])->select();

        $updateReviewFeedbackData = [];
        foreach ($reviewFeedbackList as $reviewFeedbackItem) {
            $reviewFeedbackJson = json_decode($reviewFeedbackItem['json'], true);

            $isExistChangeTime = false;
            foreach ($reviewFeedbackJson['plan_delivery_time'] as $episodeId => $value) {
                foreach ($value as $valueStepCategoryId => $time) {
                    if ($valueStepCategoryId == $stepCategoryId) {
                        $isExistChangeTime = true;
                        foreach ($childrenStepCategoryIds as $childrenStepCategoryId) {
                            //更新时间
                            $reviewFeedbackJson['plan_delivery_time'][$episodeId][$childrenStepCategoryId] = $time;
                        }
                    }
                }
            }

            if ($isExistChangeTime) {
                $reviewFeedbackItem['json'] = json_encode($reviewFeedbackJson, JSON_UNESCAPED_UNICODE);
                $updateReviewFeedbackData[] = [
                    'id' => $reviewFeedbackItem['id'],
                    'json' => $reviewFeedbackItem['json']
                ];
            }
        }


        $updateReviewFeedbackEntityData = [];
        foreach ($reviewFeedbackEntityList as $reviewFeedbackEntityItem) {
            $reviewFeedbackEntityJson = json_decode($reviewFeedbackEntityItem['json'], true);

            if (isset($reviewFeedbackEntityJson['plan_delivery_time'][$stepCategoryId])) {
                $time = $reviewFeedbackEntityJson['plan_delivery_time'][$stepCategoryId];
                foreach ($childrenStepCategoryIds as $childrenStepCategoryId) {
                    //更新时间
                    $reviewFeedbackEntityJson['plan_delivery_time'][$childrenStepCategoryId] = $time;
                }
            }

            $updateReviewFeedbackEntityData[] = [
                'id' => $reviewFeedbackEntityItem['id'],
                'json' => json_encode($reviewFeedbackEntityJson, JSON_UNESCAPED_UNICODE)
            ];

        }

        if (!empty($updateReviewFeedbackData)) {
            model(ReviewFeedbackModel::class)->saveAll($updateReviewFeedbackData);
        }

        if (!empty($updateReviewFeedbackEntityData)) {
            model(ReviewFeedbackEntityModel::class)->saveAll($updateReviewFeedbackEntityData);
        }

        return [
            'review_feedback' => $updateReviewFeedbackData,
            'review_feedback_entity' => $updateReviewFeedbackEntityData
        ];
    }

    /**
     * 同步任务上游IDs
     * @param $param
     * @return bool|int|string
     */
    public function fixSyncTaskUpstream($param)
    {
        $file = 'runtime/sync_task_upstream20231017.txt';
        if (!file_exists($file)) {
            file_put_contents($file, '1259222');
        }
        //查询执行到哪了
        $getTaskId = $param['get_task_id'] ?? 0;
        if ($getTaskId) {
            return file_get_contents($file);
        }

        $i = 1;
        while (true) {
            //加文件锁
            $fd = fopen($file, 'r+', '');
            flock($fd, LOCK_EX);
            $startTaskId = fread($fd, 9999);


            $taskList = model(TaskModel::class)->field('id')
                ->where(['id' => ['gt', $startTaskId]])
                ->where(['step_type' => 'step'])
                ->order('id asc')
                ->limit(100)
                ->column('id');

            if (empty($taskList)) {
                flock($fd, LOCK_UN);
                fclose($fd);
                return '已完成';
            }
            foreach ($taskList as $val) {
                if (empty($val)) {
                    continue;
                }
                TaskUpstreamService::getInstance()->syncTaskUpstream($val);
            }

            //写入游标
            file_put_contents($file, end($taskList));
            flock($fd, LOCK_UN);
            fclose($fd);
            //防止过快--睡眼0.1s
            usleep(1000);
            $i++;
            if ($i > 1500) {
                return "已循环{$i}次";
            }
        }

    }


    /**
     * 修复原计划节点--数据
     * @param $param
     * @return int
     */
    public function fixOriginalPlanNodeData($param)
    {
        $delete = $param['delete'] ?? 0;
        //删除原计划--重复数据
        if ($delete) {
            $this->delOriginalPlanNodeData();
            return 1;
        }

        //更新排期数据
        $planTypeList = [OriginalPlanNodeModel::TYPE_STANDARD_PLAN, OriginalPlanNodeModel::TYPE_INNER_PLAN];

        $condition[]['all_task_ids'] = ['EXP', 'IS NULL'];
        $condition[]['all_task_ids'] = '';
        $condition['_logic'] = "OR";
        $where[] = $condition;

        $list = model(OriginalPlanNodeModel::class)->where($where)->where(['type' => ['in', $planTypeList]])->select();

        foreach ($list as $val) {
            OriginalPlanNodeService::getInstance()->fixOriginalPlanNodeData($val['link_id'], $val['id']);
        }
        return 1;
    }


    /**
     * 删除原计划--重复数据
     * @return int
     */
    private function delOriginalPlanNodeData(int $time, int $endTime=0)
    {
        $condition = [];
        $condition['delivery_time'] = ['GT', $time];
        if ($endTime) {
            $condition['delivery_time'] = ["BETWEEN", [$time, $endTime]];
        }

        $list = model(OriginalPlanNodeModel::class)->where($condition)->select();

        foreach ($list as $val) {
            OriginalPlanNodeService::getInstance()->delOriginalPlanNodeData($val);
        }
        return 1;
    }


    /**
     * 修复原计划节点--补充旧数据
     * @param $param
     * @return int
     */
    public function fixOriginalPlanNodeOldData($param)
    {
        $now = time();

        $fixType = $param['fix_type'] ?? '';
        //定时脚本执行
        if (empty($fixType)) {

            $time = strtotime("-1 day");
            //补漏--排期节点、延期单节点、需求单节点
            $this->fixOriginalPlanNodePlanData($time);
            $this->fixOriginalPlanNodeDelayOrderData($time);
            $this->fixOriginalPlanNodeReviewFeedbackData($time);
            //切割切点--需求单、排期
            $this->fixSplitReviewFeedbackData($time);
            $this->splitOriginalPlanNode($time);

            //删除重复数据
            $this->delOriginalNodeRepeatData($time);
            //
            $this->updateNodeDetailCloseType($time);

            //生成原计划节点--详细表
            Client::send('sync_original_plan_node_detail', build_queue_data_with_xu(['start_time' => $time]));

            return 1;
        }

        if (isset($param['start_time'])) {
            $startTime = $param['start_time'];
        } else {
            return 0;
        }

        //指定修复--排期数据
        if ($fixType == 'plan') {
            $this->fixOriginalPlanNodePlanData($startTime);
        }
        //指定修复--延期单
        if ($fixType == 'delay_order') {
            $this->fixOriginalPlanNodeDelayOrderData($startTime);
        }
        //指定修复--需求单
        if ($fixType == 'review_feedback') {
            $this->fixOriginalPlanNodeReviewFeedbackData($startTime);
        }

        //指定修复--需求单--切割
        if ($fixType == 'split_review_feedback') {
            //group长度限制的数据
            $this->repairOriginalPlanNode([5, 6]);
            $this->repairOriginalPlanNode();
            //切割需求单节点
            $this->fixSplitReviewFeedbackData($startTime);
        }

        //指定修复--标准排期数据
        if ($fixType == 'repair_plan') {
            $this->fixStandardPlanNodeData();
        }

        //指定修复--标准排期数据--去除模版待办主任务
        if ($fixType == 'repair_plan_todo') {
            return $this->fixStandardPlanNodeTodoData();
        }

        //删除--需求单--延期单数据
        if ($fixType == 'del_review_feedback_delay') {
            return $this->delOriginalPlanNodeDelayOrder();
        }

        //关闭--需求单
        if ($fixType == 'close_review_feedback') {
            $reviewFeedbackId = $param['review_feedback_id'];
            if (empty($reviewFeedbackId)) {
                return 0;
            }
            Client::send('check_demand_order_closed', build_queue_data_with_xu(['review_feedback_id' => $reviewFeedbackId]));
            return 1;
        }

        //拆分节点--更新节点--工序ID、目标环节、节点类型
        if ($fixType == 'split_original_plan_node') {
            return $this->splitOriginalPlanNode($startTime);
        }

        //修补需求池统计--旧数据
        if ($fixType == 'sync_demand_pool_report') {
            return $this->fixDemandPoolReportData($startTime, $param['end_time']);
        }

        //生成原计划节点详细表
        if ($fixType == 'generate_original_node_daily') {
            return $this->generateOriginalNodeDaily($startTime, $param['limit']);
        }

        //更新原计划节点详细表
        if ($fixType == 'update_node_detail_close_type') {
            return $this->updateNodeDetailCloseType();
        }

        return 1;
    }


    /**
     * 修补排期的数据
     * @param int $time
     * @return int
     */
    private function fixOriginalPlanNodePlanData(int $time)
    {
        //更新排期数据
        $condition = [];
        $condition['scope'] = PlanModel::SCOPE_SINGLE;
        $condition['created'] = ['GT', $time];

        $list = model(PlanModel::class)
            ->where(["_string" => "id not in (SELECT link_id from original_plan_node where type in (1,2) )"])
            ->where($condition)
            ->field("id")
            ->select();
        foreach ($list as $val) {
            OriginalPlanNodeService::getInstance()->fixOriginalPlanNodePlanData($val['id']);
        }
        return 1;
    }

    /**
     * 修补排期的数据
     * @return int
     */
    private function repairOriginalPlanNodePlanData()
    {
        //更新排期数据
        $condition = [];
        //组内排期
        $condition['opn.type'] = 2;

        //判断是否已有节点
        $list = model(OriginalPlanNodeModel::class)->alias('opn')
            ->join("plan p on opn.link_id = p.id")
            ->where($condition)
            ->field('opn.id,p.type')
            ->select();

        $list = array_filter($list, function ($item) {
            return $item['type'] == 'to_do';
        });

        $ids = array_column($list, 'id');
        if (empty($ids)) {
            return 2;
        }
        model(OriginalPlanNodeModel::class)->where(['id' => ['in', $ids]])->save(['type' => 1]);

        return 1;
    }

    /**
     * 修补延期单的数据
     * @param int $time
     * @return int
     */
    private function fixOriginalPlanNodeDelayOrderData(int $time)
    {
        //更新排期数据
        $condition = [];
        $condition['do.created'] = ['GT', $time];
        $condition['do.is_reviewed'] = 'yes';
        $condition['do.confirm_status'] = 'yes';

        $list = model(DelayOrderModel::class)->alias('do')
            ->join("delay_order_task dot on dot.delay_order_id = do.id")
            ->where(["_string" => "do.id not in (SELECT link_id from original_plan_node where type in (5,6) )"])
            ->where($condition)
            ->field("do.id")
            ->select();
        foreach ($list as $val) {
            OriginalPlanNodeService::getInstance()->saveOriginalPlanNoteByDelayOrder($val['id'], 1);
        }
        return 1;
    }

    /**
     * 修补需求单的数据
     * @param int $time
     * @return int
     */
    private function fixOriginalPlanNodeReviewFeedbackData(int $time)
    {
        //更新排期数据
        $condition = [];
        $condition['created'] = ['GT', $time];
        $condition['status'] = ReviewFeedbackModel::STATUS_FINAL;

        $list = model(ReviewFeedbackModel::class)
            ->where($condition)
            ->where(["_string" => "id not in (SELECT link_id from original_plan_node where type in (3,4) )"])
            ->field("id")
            ->select();
        foreach ($list as $val) {
            OriginalPlanNodeService::getInstance()->addOriginalPlanNotes($val['id'], 1);
        }
        return 1;
    }


    /**
     * 修补需求单的数据
     * @param int $time
     * @return int
     */
    private function fixSplitReviewFeedbackData(int $time)
    {
        //需求单数据
        $condition = [];
        $condition['delivery_time'] = ['GT', $time];
        $condition['type'] = ['in', [3, 4]];

        //判断是否已有节点
        $list = model(OriginalPlanNodeModel::class)->where($condition)->select();
        if (empty($list)) {
            return 0;
        }

        foreach ($list as $val) {
            $taskIds = explode(',', $val['all_task_ids']);
            if (empty($taskIds)) {
                continue;
            }
            //根据任务时间生成节点
            OriginalPlanNodeService::getInstance()->splitReviewFeedbackData($taskIds, $val);
        }
        return 1;
    }

    /**
     * 删除--需求单产生的--延期数据
     * @return int
     */
    private function delOriginalPlanNodeReviewFeedback()
    {
        //需求单数据
        $condition = [];
        $condition['opn.type'] = ['in', [5, 6]];

        //判断是否已有节点
        $dotTaskList = model(OriginalPlanNodeModel::class)->alias('opn')
            ->join('delay_order_task dot on opn.link_id = dot.delay_order_id', 'left')
            ->field('opn.id')
            ->where($condition)
            ->where("dot.type='demand_order'")
            ->select();
        $ids = array_column($dotTaskList, 'id');
        model(OriginalPlanNodeModel::class)->where(['id' => ['IN', $ids]])->delete();

        return 1;
    }

    /**
     * 删除--延期单产生的垃圾数据
     * @return int
     */
    private function delOriginalPlanNodeDelayOrder()
    {
        //需求单数据
        $condition = [];
        $condition['opn.type'] = ['in', [5, 6]];

        //判断是否已有节点
        $dotTaskList = model(OriginalPlanNodeModel::class)->alias('opn')
            ->join('delay_order_task dot on opn.link_id = dot.delay_order_id', 'left')
            ->field('opn.id')
            ->where('dot.delay_time <= dot.plan_delivery_time')
            ->where($condition)
            ->select();

        $ids = array_column($dotTaskList, 'id');
        model(OriginalPlanNodeModel::class)->where(['id' => ['IN', $ids]])->delete();
        return 1;
    }

    /**
     * 修复 group长度限制的数据
     * @return int
     */
    private function repairOriginalPlanNode($typeList = [])
    {
        if (empty($typeList)) {
            $typeList = [3, 4];
        }
        //需求单数据
        $condition = [];
        $condition['type'] = ['in', $typeList];

        //查询超了的记录
        $dotTaskList = model(OriginalPlanNodeModel::class)
            ->field('id,link_id,type')
            ->where("LENGTH(all_task_ids)=1024")
            ->where($condition)
            ->select();
        if (empty($dotTaskList)) {
            return 2;
        }

        //先删除
        $linkIds = array_column($dotTaskList, 'link_id');
        model(OriginalPlanNodeModel::class)->where(['link_id' => ['IN', $linkIds], 'type' => ['in', $typeList]])->delete();

        //再增加
        foreach ($dotTaskList as $val) {
            //需求单
            if (in_array($val['type'], [3, 4])) {
                OriginalPlanNodeService::getInstance()->addOriginalPlanNotes($val['link_id'], 1);
            }
            //延期单
            if (in_array($val['type'], [5, 6])) {
                OriginalPlanNodeService::getInstance()->saveOriginalPlanNoteByDelayOrder($val['link_id'], 1);
            }
        }

        return 1;
    }

    /**
     * 修补租户id为0的数据--更新租户ID
     * @return int
     */
    private function fixOriginalPlanTenantData()
    {
        //需求单数据
        $condition = [];
        $condition['opn.tenant_id'] = 0;

        //查询租户ID为0的数据
        $list = model(OriginalPlanNodeModel::class)->alias('opn')
            ->join("project pro on pro.id = opn.project_id", "LEFT")
            ->field("opn.id, pro.tenant_id")
            ->where($condition)
            ->limit(C("database.database_max_select_rows"))
            ->select();

        if (empty($list)) {
            return 0;
        }
        $data = [];
        foreach ($list as $val) {
            $data[] = ['id' => $val['id'], 'tenant_id' => $val['tenant_id']];
        }
        model(OriginalPlanNodeModel::class)->saveAll($data);
        return 1;
    }

    /**
     * 去除--标准排期节点--模板代办主任务
     * @return int
     */
    private function fixStandardPlanNodeTodoData()
    {
        //标准排期--数据
        $condition = [];
        $condition['type'] = 1;

        //查询-标准排期--数据
        $list = model(OriginalPlanNodeModel::class)->field('id,all_task_ids')->where($condition)->select();
        if (empty($list)) {
            return 0;
        }

        $taskModel = model(TaskModel::class);
        foreach ($list as $val) {
            if (empty($val['all_task_ids'])) {
                continue;
            }
            $taskIds = explode(',', $val['all_task_ids']);

            //查询是否有-- 模板代办主任务---有才更新
            $hasOne = $taskModel->where(['id' => ['IN', $taskIds], 'step_type' => TaskModel::STEP_TYPE_TEMPLATE_TO_DO])->field('id')->find();
            if ($hasOne) {
                $allTaskIds = $taskModel->where(['id' => ['IN', $taskIds], 'step_type' => ['neq', TaskModel::STEP_TYPE_TEMPLATE_TO_DO]])->field('id')->column('id');
                //更新
                model(OriginalPlanNodeModel::class)->where(['id' => $val['id']])->save(['all_task_ids' => implode(',', $allTaskIds)]);
            }
        }
        return 'OK';
    }

    /**
     * 修补标准排期--数据
     * @return int
     */
    private function fixStandardPlanNodeData()
    {
        //需求单数据
        $condition = [];
        $condition['type'] = 1;

        //判断是否已有节点
        $list = model(OriginalPlanNodeModel::class)->where($condition)->select();
        if (empty($list)) {
            return 0;
        }

        foreach ($list as $val) {
            $taskIds = explode(',', $val['all_task_ids']);
            if (count($taskIds) <= 1) {
                continue;
            }
            //标准排期--根据任务清单--切割原计划节点
            OriginalPlanNodeService::getInstance()->splitStandardPlanData($val['id']);
        }
    }

    /**
     * 修补需求池的数据
     * @return int
     */
    private function fixDemandPoolReportData(int $time, int $endTime=0)
    {
        $condition = [];
        $condition['scope'] = PlanModel::SCOPE_GLOBAL;
        $condition['created'] = ['GT', $time];
        if ($endTime) {
            $condition['created'] = ["BETWEEN", [$time, $endTime]];
        }

        $list = model(PlanModel::class)
            ->field('id')
            ->where($condition)
            ->select();
        foreach ($list as $val) {
            DemandPoolReportService::getInstance()->syncDemandPoolByPlanId($val['id']);
        }
        return 1;
    }

    /**
     * 删除重复数据
     * @param int $time
     * @return int
     */
    public function delOriginalNodeRepeatData(int $time)
    {
        //标准排期数据
        $condition = [];
        $condition['delivery_time'] = ['GT', $time];

        //重复的数据量不到20条
        $list = model(OriginalPlanNodeModel::class)
            ->where($condition)
            ->field("link_id,step_category_id,count(*) as num,all_task_ids")
            ->group("all_task_ids, step_category_id, link_id")
            ->having("num>1")
            ->select();

        foreach ($list as $val) {
            if ($val['num'] == 2) {
                //删除一条重复数据
                $this->delRepeatStandardPlanNodeData($val);
            }
        }
        return 1;
    }

    /**
     * 删除一条重复数据
     * @param $info
     * @return int
     */
    private function delRepeatStandardPlanNodeData($info)
    {
        if (empty($info['all_task_ids'])) {
            return 1;
        }
        $taskIds = explode(',', $info['all_task_ids']);
        //查询任务--交付时间
        $taskInfo = model(TaskModel::class)->where(['id' => ['IN', $taskIds]])->field('plan_delivery_time')->find();
        //查询节点交付时间
        $list = model(OriginalPlanNodeModel::class)
            ->where(['link_id' => $info['link_id'], 'all_task_ids' => $info['all_task_ids'], 'step_category_id' => $info['step_category_id']])
            ->field('id,delivery_time')
            ->select();
        //少于一条--不处理
        if (count($list) <= 1) {
            return 1;
        }
        //删除重复、错误节点ID
        $deleteId = 0;
        foreach ($list as $val) {
            //节点交付时间 != 任务--交付时间
            if ($val['delivery_time'] != $taskInfo['plan_delivery_time']) {
                $deleteId = $val['id'];
            }
        }
        //没有代表有重复节点
        if (empty($deleteId)) {
            $deleteId = $list[0]['id'] ?? 0;
        }

        model(OriginalPlanNodeModel::class)->where(['id' => $deleteId])->delete();
        //删除原计划--节点详细记录
        model(OriginalPlanNodeDetailModel::class)->where(['original_plan_node_id' => $deleteId])->delete();
        return 1;
    }

    /**
     * 生成每日异常任务报表
     * @param int $time
     * @return int
     */
    private function generateNodeDailyReport(int $time)
    {
        //标准排期数据
        $condition = [];
        $condition['delivery_time'] = ['GT', $time];
        $condition['status'] = OriginalPlanNodeModel::STATUS_EXEC;

        //查询原计划--数据
        $list = model(OriginalPlanNodeModel::class)
            ->where($condition)
            ->field("id,all_task_ids,delivery_task_ids,project_id,node_type,delivery_time,target_step_category_ids")
            ->select();

        $nodeReportModel = model(OriginalPlanNodeDailyReportModel::class);
        foreach ($list as $val) {
            //异常任务
            $allTaskIdList = explode(',', $val['all_task_ids']);
            $deliveryTaskIdList = explode(',', $val['delivery_task_ids']);
            //得到-异常任务IDs = 所有任务ids-交付任务ids
            $tempList = array_diff($allTaskIdList, $deliveryTaskIdList);
            if (empty($tempList)) {
                continue;
            }
            //异常任务--上报日期
            $reportDate = strtotime(date('Y-m-d', $val['delivery_time']));
            //有异常任务--保存
            $nodeReport = $reportData = [];
            $nodeReport['project_id'] = $val['project_id'];
            $nodeReport['original_plan_node_id'] = $val['id'];

            $nodeReport['effect_step_category_ids'] = $val['target_step_category_ids'];
            $nodeReport['node_type'] = $val['node_type'];
            $nodeReport['report_date'] = $reportDate;
            foreach ($tempList as $taskId) {
                //判断是否已有记录
                $hasOne = $nodeReportModel->where(['task_id' => $taskId, 'report_date' => $reportDate])->find();
                if ($hasOne) {
                    continue;
                }
                //查询异常信息
                $abnormalList = OriginalPlanNodeService::getInstance()->getTaskAbnormalInfo($taskId, $val['id'], $val['node_type']);
                if ($abnormalList) {
                    $nodeReport['task_id'] = $taskId;
                    $nodeReport['entity_id'] = $abnormalList['desc']['entity_id'] ?? 0;
                    $nodeReport['step_category_id'] = $abnormalList['desc']['step_category_id'] ?? 0;
                    $nodeReport['abnormal_type'] = $abnormalList['abnormal_type'] ?? 0;
                    $nodeReport['last_node_id'] = $this->getLastNodeId($taskId, $val['id']);
                    $nodeReport['desc'] = json_encode($abnormalList['desc'] ?? []);
                    $reportData[] = $nodeReport;
                }
            }
            if ($reportData) {
                $nodeReportModel->addAll($reportData);
            }
        }

        //今天申请的延期数据
        $this->generateNodeDailyReportDelay($time);
        return 1;
    }

    /**
     * 生成计划外延期单  异常任务
     * @param int $time
     * @return int
     */
    private function generateNodeDailyReportDelay(int $time)
    {
        //今天申请的延期数据
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $where = [];
        //查询条件  ---  查询--延期单创建时间--在这个范围-
        $where['dot.created'] = ['GT', $time];
        $where["t.json->>'$.task_status'"] = ['neq', $closedStatus['id']];

        //查询节点列表
        $list = model(DelayOrderTaskModel::class)->alias('dot')
            ->join("task t on t.id = dot.task_id", 'LEFT')
            ->where($where)
            ->field("dot.project_id,dot.task_id,dot.created,t.entity_id,t.step_id,t.step_category_id,dot.type,dot.delay_reason,dot.delay_reason_type")
            ->select();

        $nodeReportModel = model(OriginalPlanNodeDailyReportModel::class);
        foreach ($list as $val) {
            //判断是否已有记录
            $reportDate = strtotime(date('Y-m-d', $val['created']));
            $hasOne = $nodeReportModel->where(['task_id' => $val['task_id'], 'report_date' => $reportDate])->find();
            if ($hasOne) {
                continue;
            }
            //查询交付环节
            $targetStepCategoryIds = OriginalPlanNodeService::getInstance()->getTargetStepCategory($val['project_id'], $val['task_id']);
            //节点类型--有交付环节==交付节点---
            $nodeType = OriginalPlanNodeModel::NODE_TYPE_FINISH;
            if ($targetStepCategoryIds) {
                $nodeType = OriginalPlanNodeModel::NODE_TYPE_DELIVERY;
            }
            //保存--计划外延期
            $nodeReport = [];
            $nodeReport['project_id'] = $val['project_id'];
            $nodeReport['step_category_id'] = $val['step_category_id'] ?? 0;
            $nodeReport['entity_id'] = $val['entity_id'] ?? 0;
            $nodeReport['task_id'] = $val['task_id'];

            $nodeReport['report_date'] = $reportDate;
            $nodeReport['effect_step_category_ids'] = $targetStepCategoryIds;
            $nodeReport['node_type'] = $nodeType;
            $nodeReport['abnormal_type'] = OriginalPlanNodeDailyReportModel::ABNORMAL_TYPE_OUTSIDE_PLAN;
            $desc = [];
            $desc['delay_list'] = [
                'id' => $val['project_id'],
                'task_id' => $val['task_id'],
                'type' => $val['type'],
                'delay_reason' => $val['delay_reason'],
                'delay_reason_type' => $val['delay_reason_type'],
            ];
            $nodeReport['desc'] = json_encode($desc);
            $nodeReportModel->add($nodeReport);
        }
        return 1;
    }

    /**
     * 获取任务的上一个节点
     * @param int $taskId
     * @param int $nodeId
     * @return int
     */
    private function getLastNodeId(int $taskId, int $nodeId)
    {
        //查询上一个节点ID--（晚上脚本执行）
        $lastNodeInfo = model(OriginalPlanNodeModel::class)
            ->where("find_in_set({$taskId}, all_task_ids)")
            ->where(['id' => ['lt', $nodeId]])
            ->order('id desc')
            ->field('id')
            ->find();

        return $lastNodeInfo['id'] ?? 0;
    }

    /**
     * 拆分节点--更新节点--工序ID、目标环节、节点类型
     * @param int $time
     * @return int
     */
    private function splitOriginalPlanNode(int $time)
    {
        //查询条件
        $condition = [];
        $condition['delivery_time'] = ['GT', $time];
        $condition['step_id'] = 0;

        $orgNodeModel = model(OriginalPlanNodeModel::class);
        //查询记录
        $list = $orgNodeModel->field('id')->where($condition)->select();

        foreach ($list as $val) {
            //根据任务工序ID切割节点
            OriginalPlanNodeService::getInstance()->splitOriginalPlanNodeByStepId($val['id']);
        }
        return 1;
    }

    /**
     * 生成--节点详情表
     * @param int   $time 交付时间
     * @param int   $limit 限制多少条
     * @return string
     */
    public function generateOriginalNodeDaily(int $time, int $limit = 500)
    {
        //查询条件--未同步的节点、任务ID不为空
        $condition = [];
        $condition['delivery_time'] = ['GT', $time];
        $condition['is_sync_detail'] = OriginalPlanNodeModel::IS_SYNC_DETAIL_NO;
        $condition['all_task_ids'] = ['neq', ''];

        $orgNodeModel = model(OriginalPlanNodeModel::class);
        //查询记录
        $list = $orgNodeModel->field('id')->where($condition)->order('id asc')->limit($limit)->select();
        if (empty($list)) {
            return 'Finish';
        }

        foreach ($list as $val) {
            //根据节点ID--同步--原计划节点详细表
            OriginalPlanNodeService::getInstance()->syncOriginalPlanNodeDetailById($val['id']);
        }
        return 'OK';
    }

    /**
     * 任务完成--同步到节点详细表
     * @return string
     */
    public function updateNodeDetailCloseType(int $time=0)
    {
        $orgNodeDetailModel = model(OriginalPlanNodeDetailModel::class);

        //任务完成状态
        $statusDone = StatusService::getInstance()->getTaskDoneStatus();

        //更新--否存在过去时间内未关闭的交付节点
        $condition = [
            //交付状态
            'opn.delivery_status' => OriginalPlanNodeDetailModel::DELIVERY_STATUS_DEFAULT,
            //任务完成状态
            "task.json->>'$.task_status'" => $statusDone['id']
        ];
        if ($time) {
            $condition['original_delivery_time'] = ['gt', $time];
        }
        //查询记录
        $list = $orgNodeDetailModel
            ->alias('opn')
            ->join('task on task.id = opn.task_id')
            ->where($condition)
            ->limit(5)
            ->field('task.id')
            ->select();


        foreach ($list as $val) {
            //根据节点ID--同步--原计划节点详细表
            OriginalPlanNodeService::getInstance()->updateNodeDetailCloseType($val['id']);
        }
        return 'OK';
    }

}
